【问题标题】:New instance of class with a non-None class attribute?具有非无类属性的类的新实例?
【发布时间】:2011-12-15 18:33:37
【问题描述】:

我有一个 Python 类,它的类属性设置为 None 以外的值。创建新实例时,对该属性所做的更改会在所有实例中持续存在。

这里有一些代码可以理解这一点:

 class Foo(object):
   a = []
   b = 2

 foo = Foo()
 foo.a.append('item')
 foo.b = 5

使用foo.a 返回['item']foo.b 返回5,正如人们所期望的那样。

当我创建一个新实例(我们称之为bar)时,使用bar.a 会返回['item']bar.b 也会返回5!但是,当我最初将所有类属性设置为 None 然后将它们设置为 __init__ 中的任何内容时,如下所示:

 class Foo(object):
   a = None
   b = None

   def __init__(self):
     self.a = []
     self.b = 2

使用bar.a 返回[]bar.b 返回2foo.a 返回['item']foo.b 返回5

假设是这样工作的吗?在我编写 Python 的 3 年里,我显然从未遇到过这个问题,希望得到一些澄清。我在文档中的任何地方也找不到它,所以如果可能的话,给我一个参考会很棒。 :)

【问题讨论】:

    标签: python python-2.7 class-instance-variables


    【解决方案1】:

    是的,这就是它应该的工作方式。

    如果ab属于Fooinstance,那么正确的做法是:

    class Foo(object):
       def __init__(self):
         self.a = []
         self.b = 2
    

    以下使ab 属于类本身,因此所有实例共享相同的变量:

    class Foo(object):
       a = []
       b = 2
    

    当你混合这两种方法时——就像你在第二个例子中所做的那样——这不会增加任何有用的东西,只会引起混乱。

    值得一提的是,当您在第一个示例中执行以下操作时:

    foo.b = 5
    

    你没有改变Foo.b,你正在为foo添加一个“阴影”Foo.b的全新属性。当你这样做时,bar.bFoo.b 都不会改变。如果您随后执行del foo.b,则会删除该属性,foo.b 将再次引用Foo.b

    【讨论】:

    • 啊,我看到了曙光!直到现在我才意识到类和实例属性之间的区别!我一直假设类属性是在类本身中定义的(例如示例),并且会使用 foo.c = 2 之类的东西调用实例(如果您愿意的话,这是一个即时定义)。说得通!谢谢!
    【解决方案2】:

    是的,这正是您所期望的。当您在类上定义变量时,这些属性将附加到类,而不是实例。您可能并不总是注意到,当您分配 给实例上的属性时,该实例会获取新值,从而掩盖类属性。 就地修改的方法,比如list.append不会给实例一个新属性,因为它们只是修改现有对象,而这恰好是类的一个属性。

    任何时候类的每个实例都应该有自己的、唯一的属性值,您通常应该在 __init__ 方法中设置它,以确保每个实例的值都不同。

    只有当一个类的属性具有合理的默认值,并且该值是不可变类型(不能就地修改)时,例如intstr,才应该在类。

    【讨论】:

    • 谢谢!就像我在上面的答案中所说,我显然不知道类和实例属性之间的区别!我想这可能是自学成才和不耐烦的警告之一。 ;)
    【解决方案3】:

    是的,一开始似乎令人惊讶,但正如其他答案中所说,这是 python 的工作方式。三年蟒蛇不被此斩首?你很幸运,非常幸运。如果我是你,我会检查小动物的敏感过去代码......

    还要小心函数调用:def func(list=[]) 有类似的行为,这可能会令人惊讶。

    并且,如果可能的话,在你的提交钩子中插入一个 pylint 解析。或者至少在你的代码上运行 pylint ,它很有启发性,并且可能会告诉你这个技巧(这不是一个技巧,事实上,它是非常逻辑和正交的,只是 python 方式)。

    【讨论】:

      【解决方案4】:

      您将类属性与实例属性混淆了。在您的第一个示例中,只有类属性 ab,但在第二个示例中,您也有同名的实例属性。

      如果没有实例属性,Python 似乎会将一个属性的引用从一个实例传递给一个类(我今天学到了一些新东西!)。

      您可能会发现这段代码很有启发性:

      class Foo:
        a = 1
        b = 2
      
        def __init__(self):
          self.a=3
      
      print Foo.a   # => 1
      print Foo().a # => 3
      print Foo.b   # => 2
      print Foo().b # => 2
      

      【讨论】:

      • 谢谢!我肯定把这两者搞混了!
      猜你喜欢
      • 1970-01-01
      • 2018-04-17
      • 1970-01-01
      • 2022-06-15
      • 2013-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多