Является ли pythonic для функции, которая возвращает несколько значений? - PullRequest
78 голосов
/ 15 сентября 2008

В python у вас может быть функция, возвращающая несколько значений. Вот надуманный пример:

def divide(x, y):
    quotient = x/y
    remainder = x % y
    return quotient, remainder  

(q, r) = divide(22, 7)

Это кажется очень полезным, но похоже, что им также можно злоупотреблять («Ну ... функция X уже вычисляет то, что нам нужно в качестве промежуточного значения. Давайте сделаем так, чтобы X также вернул это значение»).

Когда вы должны нарисовать линию и определить другой метод?

Ответы [ 9 ]

104 голосов
/ 15 сентября 2008

Абсолютно (для приведенного вами примера).

Кортежи являются первоклассными гражданами в Python

Существует встроенная функция divmod(), которая делает именно это.

q, r = divmod(x, y) # ((x - x%y)/y, x%y) Invariant: div*y + mod == x

Существуют и другие примеры: zip, enumerate, dict.items.

for i, e in enumerate([1, 3, 3]):
    print "index=%d, element=%s" % (i, e)

# reverse keys and values in a dictionary
d = dict((v, k) for k, v in adict.items()) # or 
d = dict(zip(adict.values(), adict.keys()))

Кстати, скобки в большинстве случаев не нужны. Цитата из Справочник по библиотеке Python :

Кортежи могут быть построены несколькими способами:

  • Использование пары скобок для обозначения пустого кортежа: ()
  • Использование запятой для одиночного кортежа: a или (a,)
  • Разделение элементов запятыми: a, b, c или (a, b, c)
  • Использование встроенного tuple (): tuple () или tuple (итерируемый)

Функции должны служить одной цели

Поэтому они должны вернуть один объект. В вашем случае этот объект является кортежем. Рассмотрим кортеж как специальную составную структуру данных. Существуют языки, в которых почти каждая функция возвращает несколько значений (список в Лиспе).

Иногда достаточно вернуть (x, y) вместо Point(x, y).

Именованные кортежи

С введением именованных кортежей в Python 2.6 во многих случаях предпочтительно возвращать именованные кортежи вместо простых кортежей.

>>> import collections
>>> Point = collections.namedtuple('Point', 'x y')
>>> x, y = Point(0, 1)
>>> p = Point(x, y)
>>> x, y, p
(0, 1, Point(x=0, y=1))
>>> p.x, p.y, p[0], p[1]
(0, 1, 0, 1)
>>> for i in p:
...   print(i)
...
0
1
26 голосов
/ 15 сентября 2008

Во-первых, обратите внимание, что Python допускает следующее (без скобок):

q, r = divide(22, 7)

Что касается вашего вопроса, то в любом случае нет жесткого и быстрого правила. Для простых (и обычно надуманных) примеров может показаться, что у данной функции всегда может быть одна цель, в результате чего получается одно значение. Однако при использовании Python для реальных приложений вы быстро сталкиваетесь со многими случаями, когда необходимо возвращать несколько значений, и в результате получается более чистый код.

Итак, я бы сказал, делайте все, что имеет смысл, и не пытайтесь соответствовать искусственному соглашению. Python поддерживает несколько возвращаемых значений, поэтому используйте его при необходимости.

12 голосов
/ 15 сентября 2008

В качестве примера вы приводите встроенную функцию python, которая называется divmod. Таким образом, кто-то в какой-то момент подумал, что он достаточно питоничен для включения в базовую функциональность.

Для меня, если это делает код чище, это питон. Сравните эти два блока кода:

seconds = 1234
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)

seconds = 1234
minutes = seconds / 60
seconds = seconds % 60
hours = minutes / 60
minutes = minutes % 60
3 голосов
/ 15 сентября 2008

Да, возвращение нескольких значений (т. Е. Кортежа) определенно питонно. Как уже отмечали другие, в стандартной библиотеке Python, а также в уважаемых проектах Python есть множество примеров. Два дополнительных комментария:

  1. Возвращение нескольких значений иногда очень и очень полезно. Возьмем, к примеру, метод, который опционально обрабатывает событие (при этом возвращает некоторое значение), а также возвращает успех или неудачу. Это может возникнуть в цепочке ответственности. В других случаях вы хотите вернуть несколько тесно связанных частей данных - как в приведенном примере. В этом параметре возвращение нескольких значений сродни возвращению одного экземпляра анонимного класса с несколькими переменными-членами.
  2. Обработка аргументов метода в Python требует возможности прямого возврата нескольких значений. Например, в C ++ аргументы метода могут передаваться по ссылке, поэтому вы можете присваивать им выходные значения в дополнение к формальному возвращаемому значению. В Python аргументы передаются «по ссылке» (но в смысле Java, а не C ++). Вы не можете назначать новые значения аргументам метода и отражать их вне области метода. Например:

    // C++
    void test(int& arg)
    {
        arg = 1;
    }
    
    int foo = 0;
    test(foo); // foo is now 1!
    

    Сравнить с:

    # Python
    def test(arg):
        arg = 1
    
    foo = 0
    test(foo) # foo is still 0
    
1 голос
/ 13 марта 2009

OT: Algol68 RSRE имеет любопытный оператор "/: =". например.

INT quotient:=355, remainder;
remainder := (quotient /:= 113);

Дает частное 3, а остаток 16.

Примечание: обычно значение "(x /: = y)" отбрасывается, поскольку частное "x" назначается по ссылке, но в случае RSRE возвращаемое значение - остаток.

c.f. Целочисленная арифметика - Алгол68

1 голос
/ 15 сентября 2008

Возвращение кортежа это круто. Также обратите внимание на новый namedtuple который был добавлен в Python 2.6, который может сделать это более приемлемым для вас: http://docs.python.org/dev/library/collections.html#collections.namedtuple

1 голос
/ 15 сентября 2008

Это определенно питон. Тот факт, что вы можете возвращать несколько значений из функции, которая была бы у вас на языке, подобном C, где вам нужно определить структуру для каждой комбинации типов, которые вы где-то возвращаете.

Однако, если вы дойдете до точки, когда вы возвращаете что-то безумное, например, 10 значений из одной функции, вам следует серьезно рассмотреть возможность их объединения в класс, поскольку в этот момент это становится громоздким.

0 голосов
/ 16 сентября 2008

Я довольно новичок в Python, но техника кортежа мне кажется очень питонной Однако у меня была другая идея, которая может улучшить читабельность. Использование словаря позволяет получить доступ к различным значениям по имени, а не по положению. Например:

def divide(x, y):
    return {'quotient': x/y, 'remainder':x%y }

answer = divide(22, 7)
print answer['quotient']
print answer['remainder']
0 голосов
/ 15 сентября 2008

Можно возвращать несколько значений, используя кортеж для простых функций, таких как divmod. Если он делает код читабельным, это Pythonic.

Если возвращаемое значение начинает сбивать с толку, проверьте, не слишком ли много делает функция, и разбейте ее, если это так. Если большой кортеж используется как объект, сделайте его объектом. Также рассмотрите возможность использования именованных кортежей , которые будут частью стандартной библиотеки в Python 2.6.

...