Какова область действия параметра по умолчанию в Python? - PullRequest
24 голосов
/ 25 февраля 2010

Когда вы определяете функцию в 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]

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

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

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

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

Ответы [ 7 ]

23 голосов
/ 25 февраля 2010

Сфера соответствует ожидаемой.

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

Список хранится в 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]
6 голосов
/ 25 февраля 2010

Область действия переменной 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 голосов
/ 25 февраля 2010

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

m = []

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

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

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

1 голос
/ 25 февраля 2010

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

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

func()
func()
func()

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

То, что он делает с аргументами по умолчанию, является интересной проблемой. В Python 3 он размещает большую часть информации о функции в двух местах: func.__code__ и func.__defaults__. В питоне 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 создает и использует функции.

0 голосов
/ 25 февраля 2010

Вы должны помнить, что 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" имеет глобальную область видимости.

0 голосов
/ 25 февраля 2010

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

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

У Леннарта Регебро есть хорошее объяснение , а ответ на вопрос Роберто Лиффредо превосходен.

Чтобы адаптировать ответ Леннарта ... если у меня есть 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 являются объектами s.

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

0 голосов
/ 25 февраля 2010

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

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

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

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

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

...