Вызовы функций Python истекают областью действия, сохраняют состояние, не могут инициализировать параметры? - PullRequest
5 голосов
/ 06 июня 2009

Прежде чем я набрался смелости подать отчет об ошибке, я решил проверить свои предположения среди более умных Pythonistas здесь. Сегодня я столкнулся с непонятным случаем, поэтому я сократил его до игрушечного примера, показанного ниже:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

"""
A little script to demonstrate that a function won't re-initialize its
list parameters between calls, but instead allows them to retain state.

"""

def bleedscope(a=[], b=[]):
    """
    On each call, unless explicitly passed, both `a` and `b` should be
    initialized as empty lists.

    """

    c = a
    if b:
        c.extend(b)
    return len(c)


x = bleedscope(b=[1])
print x     # Should be 1, as expected.
x = bleedscope(b=[2])
print x     # Expect also to be 1, but it's 2. `a` is retained.
x = bleedscope(a=[1])
print x     # Now 1 as expected.
x = bleedscope(b=[3])
print x     # 1 as expected? No, it's 3! Insanity!

Я думал, что аргументы функции были локальными по объему для функции и собирались мусором в конце вызова функции, чтобы никогда не сохранять состояние между ними. Я протестировал вышеупомянутый скрипт на Python 2.5.2 и Python 2.6.1, и мое понимание не дает результатов. Аргумент a определенно сохраняет состояние между большинством этих вызовов; самым запутанным является последний вызов bleedscope, где он пропускает состояние предыдущего вызова и возвращается к состоянию в конце второго (то есть [1, 2]). [Я предлагаю запустить это в вашем любимом отладчике, чтобы убедиться в этом. Если у вас его нет, я предлагаю Winpdb в качестве отдельного отладчика Python для FOSS.]

Что здесь происходит?

Ответы [ 4 ]

15 голосов
/ 06 июня 2009

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

http://effbot.org/zone/default-values.htm

8 голосов
/ 06 июня 2009

Это ваша проблема:

def bleedscope(a=[], b=[]):

должно быть

def bleedscope(a=None, b=None):
    if a is None: a = []
    if b is None: b = []

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

4 голосов
/ 07 июня 2009

Это FAQ .

1 голос
/ 06 июня 2009

Как ни странно, ваш ввод и вывод очень похожи по совершенно случайным причинам.

На самом деле, что происходит с Python, так это то, что значения по умолчанию для a и b в объявлении вашего метода являются «статическими» значениями. Они создаются один раз при определении метода. Таким образом, значение по умолчанию «a» будет выдвигаться каждый раз, когда вы не передаете «a» в качестве аргумента.

Поставьте «print a» в начале вашего метода, чтобы увидеть, как это происходит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...