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

StackOverflow https://stackoverflow.com/questions/61605

Вопрос

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

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

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

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

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

Это было полезно?

Решение

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

Кортежи - это первоклассные граждане в 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(повторяемый)

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

Следовательно, они должны возвращать один объект.В вашем случае этот объект является кортежем.Рассматривайте кортеж как специальную составную структуру данных.Существуют языки, где почти каждая отдельная функция возвращает несколько значений (список в 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

Другие советы

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

q, r = divide(22, 7)

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

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

Приведенный вами пример на самом деле является встроенной функцией 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

Да, возврат нескольких значений (т. е. кортежа) определенно является pythonic.Как указывали другие, в стандартной библиотеке 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
    

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

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

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

ОТ:В Algol68 RSRE есть любопытный оператор "/:=".например.

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

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

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

с.ф. Целочисленная арифметика - Algol68

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

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

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

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

answer = divide(22, 7)
print answer['quotient']
print answer['remainder']
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top