Изменить объект непосредственно в функции - это анти-паттерн в Python? - PullRequest
1 голос
/ 24 октября 2019

Предположим, что мы должны получить какое-то значение и изменить его из функции.

Way-1

def change_b(obj):
    obj['b'] = 4


result = {'a': 1, 'b': 2}
change_b(obj=result)
print(result)

Как вы знаете, эта функция change_b() изменяетсяЗначение result['b'] непосредственно в функции.

Way-2

from copy import deepcopy


def change_b(obj):
    temp = deepcopy(obj)
    temp['b'] = 4
    return temp


result = {'a': 1, 'b': 2}
result = change_b(obj=result)
print(result)

Но Way-2 копирование объекта в новый объект изаменить значение из нового объекта.

Итак, оригинальный объект ни на что не влияет. (Кроме того, без побочных эффектов)

Может быть Way-2 более безопасен, потому что он не меняет оригинальный объект.

Интересно, какой из них болееобщий и питонский способ?

спасибо.

Ответы [ 3 ]

1 голос
/ 24 октября 2019

"Явное лучше, чем неявное"
...
"Перед лицом двусмысленности откажитесь от искушения угадать."

- PEP 20


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

В этом случае ваш Way-1 проще и понятнее. Очевидно, что переменная будет изменена, и способ ее изменения можно легко определить, посмотрев на код.

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

Например, встроенная в Python структура данных set имеет методы-аналоги: set.difference(other) и set.difference_update(other). В обоих случаях они делают одно и то же: вычисляют разницу между этим набором и данным набором. В первом случае этот результат возвращается без изменения исходного набора. В последнем случае исходный набор изменяется и ничего не возвращается. Очень просто выяснить, что и для чего.

В общем, вам, вероятно, следует избегать обновления значений и , возвращающих это же значение, потому что это более неоднозначно. Обратите внимание, что большинство методов python делают один или другой, но не оба (и те, которые делают оба, например list.pop(), делают это разумно, при этом возвращаемый объект не является объектом, который был изменен).

1 голос
/ 24 октября 2019

Сводка

Если API явно указывает, что он обновляет свои входные данные, Way-1 хорошо и желательно: add_route(route_map, new_route).

Если APIпрежде всего о чем-то другом, тогда Way-2 избегает непреднамеренных побочных эффектов.

Примеры в Python

Way-1 : dict.update () и list.sort () выполняют обновления на месте, поскольку это их основная задача.

Way-2 :Встроенная функция sorted () создает новый отсортированный список из своих входных данных, который он старается не изменять. Примерно, это делает это:

def sorted(iterable, *, key=None, reverse=False):
    result = list(iterable)                # copy the data
    result.sort(key=key, reverse=reverse)  # in-place sort
    return result

Надеюсь, что проясняет, когда копировать и когда мутировать на месте: -)

0 голосов
/ 24 октября 2019

Как я понимаю Python, самый питонский способ решения этой проблемы - прояснить, что происходит. Пока вы делаете это, я не верю, что это имеет значение.

my_dict = {'a': 3, 'b': 4}
double_values_in_dict(my_dict)

# Some other code

Это надуманный пример, но довольно ясно, что здесь должно произойти, даже без определения метода. Что было бы неясно, если бы вы присвоили возвращаемое значение double_values_in_dict новой переменной;Тогда я бы не знал, что вы могли сделать с исходным dict объектом, и мне пришлось бы начать копаться в этом методе, чтобы выяснить, как он на самом деле работает.

...