Какова область действия параметра по умолчанию в Python?

Когда вы определяете функцию в Python с параметром массива, какова область действия этого параметра?

Этот пример взят из учебника Python:

def f(a, L=[]):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)

Печать

[1]
[1, 2]
[1, 2, 3]

Я не совсем уверен, понимаю ли я, что происходит здесь. Означает ли это, что масштаб массива находится вне функции? Почему массив запоминает свои значения из вызова для вызова? Исходя из других языков, я бы ожидал такого поведения, только если переменная была статичной. В противном случае кажется, что он должен быть reset каждый раз. И на самом деле, когда я попробовал следующее:

def f(a):
    L = []
    L.append(a)
    return L

Я получил ожидаемое поведение (массив был reset для каждого вызова).

Итак, мне кажется, что мне просто нужна строка def f(a, L=[]): - какова область действия переменной L?

Ответ 1

Область видимости, как и следовало ожидать.

Возможно, удивительно, что значение по умолчанию вычисляется только один раз и повторно используется, поэтому каждый раз, когда вы вызываете функцию, вы получаете один и тот же список, а не новый список, инициализированный [].

Список сохраняется в f.func_defaults.

def f(a, L=[]):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)
print f.func_defaults
f.func_defaults = (['foo'],) # Don't do this!
print f(4)

Результат:

[1]
[1, 2]
[1, 2, 3]
([1, 2, 3],)
['foo', 4]

Ответ 2

Объем переменной L ведет себя так, как вы ожидаете.

"Проблема" - со списком, который вы создаете с помощью []. Каждый раз, когда вы вызываете эту функцию, Python не создает новый список. L получает назначенный один и тот же список при каждом вызове, поэтому функция "запоминает" предыдущие вызовы.

Таким образом, это то, что у вас есть:

mylist = []
def f(a, L=mylist):
    L.append(a)
    return L

Учебник по Python ставит его таким образом:

Значение по умолчанию оценивается только один раз. Это имеет значение, когда по умолчанию это изменяемый объект, такой как список, словарь или экземпляры большинства классов.

и предлагает следующий способ кодирования ожидаемого поведения:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

Ответ 3

Там еще меньше "магии", чем вы могли бы заподозрить. Это эквивалентно

m = []

def f(a, L=m):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)

m создается только один раз.

Ответ 4

Скажите, что у вас есть следующий код:

def func(a=[]):
    a.append(1)
    print("A:", a)

func()
func()
func()

Вы можете использовать отступ python, чтобы помочь вам понять, что происходит. Все, что находится на краю левого поля, выполняется при выполнении файла. Все, что отступом, скомпилировано в объект кода, который запускается при вызове func(). Таким образом, функция определена и ее аргументы по умолчанию устанавливаются один раз, когда программа запускается (потому что оператор def скрыт влево).

То, что он делает с аргументами по умолчанию, является интересной проблемой. В python 3 он предоставляет большую часть информации о функции в двух местах: func.__code__ и func.__defaults__. В python 2 func.__code__ был func.func_code func.__defaults__ был func.func_defaults. Более поздние версии python 2, включая 2.6, имеют оба набора имен, чтобы помочь перейти от python 2 к python 3. Я буду использовать более современные __code__ и __defaults__. Если вы застряли на более старшем питоне, понятия одинаковы; просто имена различаются.

Значения по умолчанию сохраняются в func.__defaults__ и извлекаются каждый раз, когда вызывается функция.

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

Играйте вокруг определения различных функций в интерактивном интерпретаторе и посмотрите, что вы можете понять о том, как создает и использует функции python.

Ответ 5

"Проблема" в том, что L=[] оценивается только один раз, то есть когда файл компилируется. Python проходит через каждую строку файла и компилирует его. К моменту достижения значения def с параметром по умолчанию он создает экземпляр списка один раз.

Если вы поместите L = [] внутри кода функции, экземпляр не будет создан во время компиляции (фактически время компиляции также можно назвать частью времени выполнения), поскольку Python компилирует код функции, но не вызывает его. Таким образом, вы получите новый экземпляр списка, потому что создание выполняется каждый раз, когда вы вызываете функцию (а не один раз во время компиляции).

Решение этой проблемы заключается в том, чтобы не использовать изменяемые объекты в качестве параметров по умолчанию или только фиксированные экземпляры типа None:

def f(a, L = None):
    if l == None:
        l = []
    L.append(a)
    return L

Обратите внимание, что в обоих случаях, которые вы описали, область L - это область функций.

Ответ 6

Объяснение дается в ответах на этот вопрос. Подводя итог этому:

Функции в Python - это своего рода объект. Поскольку они являются своего рода объектом, они действуют как объекты при создании экземпляров. Функция, если она определена с изменяемым атрибутом в качестве аргумента по умолчанию, точно такая же, как класс со статическим атрибутом, который является изменяемым списком.

Lennart Regebro имеет хорошее объяснение и ответ на вопрос Роберто Лиффредо отлично.

Чтобы адаптировать Lennart ответ... если у меня есть класс BananaBunch:

class BananaBunch:
    bananas = []

    def addBanana(self, banana):
        self.bananas.append(banana)


bunch = BananaBunch()
>>> bunch
<__main__.BananaBunch instance at 0x011A7FA8>
>>> bunch.addBanana(1)
>>> bunch.bananas
[1]
>>> for i in range(6):
    bunch.addBanana("Banana #" + i)
>>> for i in range(6):
    bunch.addBanana("Banana #" + str(i))

>>> bunch.bananas
[1, 'Banana #0', 'Banana #1', 'Banana #2', 'Banana #3', 'Banana #4', 'Banana #5']

// And for review ... 
//If I then add something to the BananaBunch class ...
>>> BananaBunch.bananas.append("A mutated banana")

//My own bunch is suddenly corrupted. :-)
>>> bunch.bananas
[1, 'Banana #0', 'Banana #1', 'Banana #2', 'Banana #3', 'Banana #4', 'Banana #5', 'A mutated banana']

Как это относится к функциям? Функции в Python - это объекты. Это повторяется. Функции в Python - это объекты.

Поэтому, когда вы создаете функцию, вы создаете объект. Когда вы даете функции изменяемое значение по умолчанию, вы заполняете этот атрибут объекта изменчивым значением и каждый раз, когда вы вызываете эту функцию, вы работаете с одним и тем же атрибутом. Поэтому, если вы используете изменяемый вызов (например, append), вы изменяете один и тот же объект, как если бы вы добавляли бананы к объекту bunch.

Ответ 7

Вы должны иметь в виду, что python является интерпретированным языком. Что здесь происходит, когда определена функция "f" , она создает список и присваивает ему параметр по умолчанию "L" функции "f" . Позже, когда вы вызываете эту функцию, тот же список используется как параметр по умолчанию. Короче говоря, код в строке "def" выполняется только один раз, когда функция определена. Это обычная пифа-питон, из которой я упал сам.

def f(a, L=[]):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)

В других ответах здесь были предложения по идиомам, чтобы исправить эту проблему. Я бы предложил следующее:

def f(a, L=None):
    L = L or []
    L.append(a)
    return L

Это использует или короткое замыкание, чтобы либо принять переданный "L", либо создать новый список.

Ответ на ваш вопрос о сфере видимости заключается в том, что "L" имеет область действия внутри функции "f" , но поскольку параметры по умолчанию назначаются один раз только одному списку, а не каждый раз, когда вы вызываете функцию, которая ведет себя так, как если бы параметр по умолчанию "L" имеет глобальную область.