【问题标题】:Python base classes share attributes? [duplicate]Python基类共享属性? [复制]
【发布时间】:2011-02-22 04:43:47
【问题描述】:

test.py 中的代码:

class Base(object):
    def __init__(self, l=[]):
        self.l = l

    def add(self, num):
        self.l.append(num)

    def remove(self, num):
        self.l.remove(num)

class Derived(Base):
    def __init__(self, l=[]):
        super(Derived, self).__init__(l)

Python shell 会话:

Python 2.6.5 (r265:79063, Apr  1 2010, 05:22:20) 
[GCC 4.4.3 20100316 (prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
>>> a = test.Derived()
>>> b = test.Derived()
>>> a.l
[]
>>> b.l
[]
>>> a.add(1)
>>> a.l
[1]
>>> b.l
[1]
>>> c = test.Derived()
>>> c.l
[1]

我期待“类似 C++”的行为,其中每个派生对象都包含自己的基类实例。还是这样吗?为什么每个对象似乎共享同一个列表实例?

【问题讨论】:

    标签: python


    【解决方案1】:

    你犯了一个常见的 Python 新手错误。

    在这里查看我的答案: How should I declare default values for instance variables in Python?

    简要说明,Python 只解释类定义一次。这意味着在__init__() 方法中声明的所有内容都只创建一次。或者,换句话说,您的[] 列表默认参数只出现一次。

    然后self.l = l 会在您每次创建新类时为同一个实例分配一个引用,因此会出现您未预料到的行为。

    Pythonic 方式是这样的(部分代码):

    def __init__(self, arg=None):
        if arg is None:
            arg = []
        self.arg = arg
    

    此外,您应该考虑使用比l 更好的命名约定,因为它难以阅读,可能会被误认为1|

    【讨论】:

    • +1 表示正确答案,但最好在此处拼出,至少简要说明一下。
    • 很明显,这是一个测试sn-p。我永远不会选择“l”作为变量名。
    • 好的,很高兴把它清理干净。
    • 我会像这样更简单地分配 arg:def __init__(self, arg=None): self.arg = arg or []
    【解决方案2】:

    这称为可变默认参数错误,通常由 Python 新手造成。当您将可变参数作为默认参数时,当需要使用默认参数时,将跨实例使用相同的对象。要更好地了解请查看http://docs.python.org/tutorial/controlflow.html#default-argument-values中的重要警告部分

    在您的代码中,实例 a 在它的 init 调用中使用了可变的默认参数(一个空列表对象),当您创建 b 的实例时,该实例又调用了 Base 的 init 方法,再次使用了与 a 相同的对象在它的初始化中使用。在更简单的单词上 a.l 和 b.l 指向同一个列表对象。

    一个非常相似的讨论 - "Least Astonishment" and the Mutable Default Argument

    【讨论】:

    • 这不是错误,这是语言的工作方式。
    猜你喜欢
    • 2017-12-30
    • 2017-10-08
    • 1970-01-01
    • 1970-01-01
    • 2018-07-03
    • 2018-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多