Является ли pythonic функцией для возврата нескольких значений?

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

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

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

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

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

Ответ 1

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

Кортежи являются гражданами первого класса в 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, b, c или(). Один кортеж должен иметь конечные запятые, такие как (d).

Функции должны обслуживать одноцелевые

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

Иногда достаточно вернуть (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

Ответ 2

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

q, r = divide(22, 7)

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

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

Ответ 3

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

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

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

Ответ 4

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

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

    // 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
    

Ответ 5

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

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

Ответ 7

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

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

answer = divide(22, 7)
print answer['quotient']
print answer['remainder']

Ответ 8

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

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

Давая фактор из 3 и остаток 16.

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

c.f. Арифметика целых чисел - Algol68

Ответ 9

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

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