Python function calls are bleeding scope, stateful, failing to initialize parameters?

大城市里の小女人 提交于 2019-12-10 14:26:57

问题


Before I have the audacity to file a bug report, I thought I'd check my assumptions among wiser Pythonistas here. I encountered a baffling case today, so I whittled it down to a toy example, shown below:

#!/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!

I thought function arguments were local in scope to the function, and were garbage-collected at the end of a function call, never to retain state between them. I have tested the above script on Python 2.5.2 and Python 2.6.1, though, and my understanding does not the results. Argument a certainly retains state between most of these calls; the most perplexing one being the final call to bleedscope, where it skips the state of the previous call and goes back to the state at the end of the second (i.e., [1, 2]). [I suggest running this in your favorite debugger to see for yourself. If you don't have one, I suggest Winpdb as a solid FOSS standalone Python debugger.]

What's going on here?


回答1:


In Python default parameter values only get initialized when the def call is parsed. In the case of an object (such as your lists), it gets reused between calls. Take a look at this article about it, which also provides the necessary workaround:

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




回答2:


This is your problem:

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

it should be

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

The default parameters are only executed once when the function is parsed, thus using the same 2 lists every time.




回答3:


It's a FAQ.




回答4:


Funnily enough, your input and your output are quite similar, for totally accidental reasons.

Actually what happens with Python is that the default values for a and b in your method declaration are "static" values. They are instanciated once at the method definition. So your default "a" is pushed each time you do not pass an "a" as argument.

Put a "print a" at the beginning of your method to see that happen.



来源:https://stackoverflow.com/questions/959113/python-function-calls-are-bleeding-scope-stateful-failing-to-initialize-parame

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!