Такое поведение неудивительно, если принять во внимание следующее:
Роль (2) была широко освещена в этой теме. (1), вероятно, является фактором, вызывающим удивление, поскольку это поведение не является «интуитивным» при поступлении с других языков.
(1) описано в учебнике Python по классам . При попытке присвоить значение атрибуту класса только для чтения:
... все переменные, найденные вне самой внутренней области, доступны только для чтения ( попытка написать такую переменная просто создаст новую локальную переменную в самой внутренней области, оставив неизмененную идентичную внешнюю переменную неизменной ).
Оглянитесь на исходный пример и рассмотрите приведенные выше пункты:
def foo(a=[]): a.append(5) return a
Здесь
foo
- объект, аa
- атрибутfoo
(доступен вfoo.func_defs[0]
). Посколькуa
является списком,a
является изменяемым и, таким образом, является атрибутом чтения-записиfoo
. Он инициализируется пустым списком, указанным сигнатурой при создании экземпляра функции, и доступен для чтения и записи до тех пор, пока существует функциональный объект.Вызов
foo
без переопределения значения по умолчанию использует значение по умолчанию отfoo.func_defs
. В этом случаеfoo.func_defs[0]
используется дляa
в пределах области кода объекта объекта. Изменения вa
меняютfoo.func_defs[0]
, который является частью объектаfoo
и сохраняется между выполнением кода вfoo
.Теперь сравните это с примером из документации по , эмулируя поведение аргументов по умолчанию других языков , так что значения по умолчанию для функции используются каждый раз, когда функция выполняется:
def foo(a, L=None): if L is None: L = [] L.append(a) return L
Принимая во внимание (1) и (2) , можно понять, почему это выполняет желаемое поведение:
- Когда объект функции
foo
создается,foo.func_defs[0]
установлен наNone
, неизменяемый объект.- Когда функция выполняется с настройками по умолчанию (без функции, заданной для
L
в вызове функции),foo.func_defs[0]
(None
) доступен в локальной области какL
.- После
L = []
присваивание не может преуспеть вfoo.func_defs[0]
, поскольку этот атрибут доступен только для чтения.- Per (1), новая локальная переменная с именем
L
создается в локальной области и используется для остальной части вызова функции.foo.func_defs[0]
, таким образом, остается неизменным для будущих вызововfoo
.