Почему бы тебе не заняться самоанализом?
Я действительно удивлен, что никто не выполнил проницательный самоанализ, предложенный Python (применимо 2
и 3
) к вызываемым объектам.
Учитывая простую маленькую функцию func
, определенную как:
>>> def func(a = []):
... a.append(5)
Когда Python встречает его, первое, что он сделает, это скомпилирует его, чтобы создать объект code
для этой функции. Пока этот шаг компиляции выполнен, Python оценивает *, а затем сохраняет аргументы по умолчанию (пустой список []
здесь) в самом объекте функции . Как указано в верхнем ответе: список a
теперь можно рассматривать как член функции func
.
.
Итак, давайте проведем некоторый самоанализ, до и после, чтобы проверить, как список расширяется внутри объекта функции. Я использую Python 3.x
для этого, для Python 2 применяется то же самое (используйте __defaults__
или func_defaults
в Python 2; да, два имени для одной и той же вещи).
Функция перед выполнением:
>>> def func(a = []):
... a.append(5)
...
После того как Python выполнит это определение, он примет любые заданные по умолчанию параметры (a = []
здесь) и поместит их в атрибут __defaults__
для объекта функции (соответствующий раздел: Callables):
>>> func.__defaults__
([],)
О.К., поэтому пустой список как отдельная запись в __defaults__
, как и ожидалось.
Функция после выполнения:
Давайте теперь выполним эту функцию:
>>> func()
Теперь, давайте посмотрим эти __defaults__
снова:
>>> func.__defaults__
([5],)
Удивлен? Значение внутри объекта изменяется! Последовательные вызовы функции теперь просто добавляются к этому внедренному list
объекту:
>>> func(); func(); func()
>>> func.__defaults__
([5, 5, 5, 5],)
Итак, у вас это есть, причина, по которой этот 'недостаток' происходит, заключается в том, что аргументы по умолчанию являются частью объекта функции. Здесь нет ничего странного, просто немного удивительно.
Распространенным решением для борьбы с этим является использование None
по умолчанию и затем инициализация в теле функции:
def func(a = None):
# or: a = [] if a is None else a
if a is None:
a = []
Поскольку тело функции каждый раз выполняется заново, вы всегда получаете новый новый пустой список, если для a
не было передано ни одного аргумента.
Для дальнейшей проверки того, что список в __defaults__
совпадает со списком, используемым в функции func
, вы можете просто изменить свою функцию, чтобы она возвращала id
списка a
, используемого внутри тела функции. Затем сравните его со списком в __defaults__
(позиция [0]
в __defaults__
), и вы увидите, как они действительно ссылаются на тот же экземпляр списка:
>>> def func(a = []):
... a.append(5)
... return id(a)
>>>
>>> id(func.__defaults__[0]) == func()
True
Все с силой самоанализа!
* Чтобы убедиться, что Python оценивает аргументы по умолчанию во время компиляции функции, попробуйте выполнить следующее:
def bar(a=input('Did you just see me without calling the function?')):
pass # use raw_input in Py2
Как вы заметите, input()
вызывается до того, как будет построен процесс привязки функции к имени bar
.