Правильный стиль для функций Python, которые изменяют аргумент

Я хотел бы написать функцию Python, которая мутирует один из аргументов (который является списком, т. Е. Изменяемым). Что-то вроде этого:

def change(array): array.append(4) change(array) 

Я больше знаком с передачей по значению, чем с настройкой Python (независимо от того, что вы решили назвать). Поэтому я обычно пишу такую ​​функцию следующим образом:

 def change(array): array.append(4) return array array = change(array) 

Вот мое замешательство. Поскольку я могу просто мутировать аргумент, второй метод кажется лишним. Но первый ошибается. Кроме того, моя конкретная функция будет иметь несколько параметров, только одна из которых изменится. Второй метод дает понять, какой аргумент изменяется (поскольку он присваивается переменной). Первый метод не дает никаких указаний. Есть ли конвенция? Что лучше'? Спасибо.

    3 Solutions collect form web for “Правильный стиль для функций Python, которые изменяют аргумент”

    Первый способ:

     def change(array): array.append(4) change(array) 

    это самый идиоматический способ сделать это. Как правило, в python мы ожидаем, что функция либо изменит аргументы, либо вернет что-то 1 . Причина этого в том, что, если функция ничего не возвращает, то она делает совершенно ясно, что функция должна иметь какой-то побочный эффект, чтобы оправдать ее существование (например, мутировать входы).

    С другой стороны, если вы делаете что-то второе:

     def change(array): array.append(4) return array array = change(array) 

    вы уязвимы, чтобы трудно отследить ошибки, когда изменяемый объект внезапно меняется, когда вы этого не ожидали, – «Но я думал, что change сделало копию» …

    1 Технически каждая функция возвращает что-то , что _something_ просто оказывается None

    Соглашение в Python состоит в том, что функции либо мутируют что-то, либо возвращают что-то, а не то, и другое.

    Если оба они полезны, вы условно записываете две отдельные функции с мутатором, названным для активного глагола, как change , и не-мутатор, названный для причастия, как changed .

    Практически все в встроенных и stdlib следует этой схеме. Метод list.append который вы вызываете, ничего не возвращает. То же самое с list.sort оставляет свой аргумент в одиночку и вместо этого возвращает новую отсортированную копию.

    Существует несколько исключений для некоторых специальных методов (например, __iadd__ предполагается мутировать, а затем возвращать self ), а также несколько случаев, когда, очевидно, должно быть что-то вроде мутации, а другая вещь возвращается (например, list.pop ) и для библиотек, которые пытаются использовать Python как своего рода язык, специфичный для домена, где согласование с идиомами целевого домена более важно, чем согласование с идиомами Python (например, некоторыми библиотеками выражений SQL-запросов). Как и во всех конвенциях, это соблюдается, если нет веских оснований.


    Итак, почему Python разработал этот способ?

    Ну, во-первых, это делает определенные ошибки очевидными. Если вы ожидали, что функция не мутирует и вернет значение, будет довольно очевидно, что вы ошибались, потому что вы получите ошибку, такую ​​как AttributeError: 'NoneType' object has no attribute 'foo' .

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

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

    Конечно, все это компромисс – он делает некоторый код более многословным в Python, чем в JavaScript, но это компромисс, глубоко встроенный в дизайн Python.

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

    Существует третий вариант, который вы не указали в своем вопросе. Вместо того, чтобы мутировать объект, переданный в качестве аргумента, сделайте копию этого аргумента и верните его. Это делает его чистой функцией без побочных эффектов.

     def change(array): array_copy = array[:] array_copy.append(4) return array_copy array = change(array) 
      Interesting Posts
      Python - лучший язык программирования в мире.