【问题标题】:Initializing field outside __init__初始化 __init__ 之外的字段
【发布时间】:2012-09-09 01:40:54
【问题描述】:

我需要一些帮助来了解 python 初始化的工作原理。我有一个类(Bar)和另一个类(Foo)作为字段/变量。当我尝试直接在 Bar 中(而不是在类 __init__ 中)初始化这个变量时,Bar 的所有实例都将指向同一个 Foo。但是如果我有一个 __init__ 方法,就像在 Bar2 中一样,每个 Bar2 实例都会有一个唯一的 Foo 实例。这里发生了什么?

class Foo():
    number = 0

class Bar():
    foo = Foo()

class Bar2():
    foo = None

    def __init__(self):
        self.foo = Foo()

first = Bar()
second = Bar()

print "Bar"
print first
print second
print first.foo
print second.foo

first = Bar2()
second = Bar2()

print "\nBar2"
print first
print second
print first.foo
print second.foo

输出例如是:

Bar
<\__main__.Bar instance at 0x025B2AF8>
<\__main__.Bar instance at 0x025B2B20>
<\__main__.Foo instance at 0x004A3AA8>
<\__main__.Foo instance at 0x004A3AA8>

Bar2
<\__main__.Bar2 instance at 0x025B2B48>
<\__main__.Bar2 instance at 0x025B2AF8>
<\__main__.Foo instance at 0x025B2B70>
<\__main__.Foo instance at 0x025B2B98>

使用 Bar 两个实例将引用同一个 Foo 实例。为什么?

编辑:更正了为 Bar 打印两次 first.foo 的错误。产生的行为仍然如输出中所示。

【问题讨论】:

  • 不要在 Python 2.x 中写 class Foo():。这将创建一个旧样式类。你几乎肯定不想那样。相反,您需要new style class。为此,请写class Foo(object):
  • 我不知道,谢谢你也指出了这一点。实际上我应该知道,因为变化发生在我第一次接触 Python 之前。

标签: python initialization python-2.7


【解决方案1】:

Python 是一种动态语言。在像 Java 这样的静态语言中,编译器读取代码,找到类定义,判断它们是否正确并相应地生成一些代码。在 python 中,一个类定义(或一个函数定义)只是一个声明,就像对一个变量的赋值一样。语法有点不同。

当定义一个类时,解释器运行类定义,即它运行类行之后的所有代码。如果它找到函数定义,它也会运行它们,即定义函数并将它们绑定到函数名称。由于类和函数定义与任何其他赋值一样是语句,因此您也可以在许多地方使用它们。例如如下:

def foo():
  class A: pass
  a = A()
  a.msg = "Hello"
  return a

因为 python 是鸭子类型的(如果它像鸭子一样嘎嘎叫,看起来像一只,那就是一只),函数 foo 的用户甚至不必知道类被调用了什么,他们只需要知道 foo返回一个带有成员 msg 的对象。你可以这样使用它:

a = foo()
print a.msg

因此,在您的示例中,执行 Bar 的定义时,将运行 classes 语句,包括创建 Foo 对象。当 Bar2 的定义被执行时,类语句在其中运行一个名为 init 的函数的定义。当创建对象时(在调用另一个函数__new__ 之后,这不是重点),Python 使用 this 作为要调用的函数的名称。

同样,类定义(类内部的代码,Bar 在其中创建 Foo 对象)仅在引入类时运行一次。 __init__ 每次创建新对象时都会一次又一次地调用,因此在 Bar2 中,Foo 的创建也会一次又一次地完成。

据我所知,“foo = None”是多余的,并不是真正需要的。在 python 中,您可以从任何地方添加实例变量,甚至可以从类外部添加,当然也可以从 __init__ 内部添加。

【讨论】:

  • 您是正确的, foo = None 是不必要的。我将把它留在示例中,因为我首先将它放在那里。我不太明白你解释了为什么我会看到这种行为。
【解决方案2】:

类也是对象,它们有自己的一组变量。不仅如此,当 Python 在对象中找不到变量时,它会在类中查找是否可以在其中找到它,这就是它使用的方法。由于该类在该类的所有对象之间共享,因此该类中的变量也是如此。

【讨论】:

    【解决方案3】:
    first = Bar()
    second = Bar()
    
    print "Bar"
    print first
    print second
    print first.foo
    print first.foo
    

    这里,你打印了两次 first.foo,这就是打印同一个 foo 对象的原因。

    first = Bar2()
    second = Bar2()
    
    print "\nBar2"
    print first
    print second
    print first.foo
    print second.foo
    

    这里,foo 是类 Bar2 中的一个静态变量,这就是为什么两个对象都指向同一个 foo 对象,用 second 的构造来构造。

    class Bar2():
     foo = None
    
     def __init__(self):
         self.foo = Foo()
    
    class Bar():
     def __init__(self):
       self.foo = Foo()
    

    在 Bar2 中,Bar2 的所有对象,都会有 foo 对象指向在 Bar2 的最后一个构造对象的构造中构造的同一个对象。

    在 Bar 中,所有 foo 对象对于 Bar 的每个对象都是唯一的,除非另有说明。

    【讨论】:

    • 我的示例在打印 first.foo 时有一个小错误,但我的真实代码没有,这就是我发现这种行为的方式。我认为您可能在回答中切换了 BarBar2 。当我运行更新的示例(正确打印)时,输出仍然相同。是 Bar 创建了 Foo 的静态版本吗?
    【解决方案4】:

    Bar.foo 是一个类变量。它在创建类时初始化一次。

    (请注意,您的代码也打印了两次first.foo,所以难怪输出是一样的。)

    【讨论】:

    • @HåkonK.Olafsen:是的,因为它是一个类变量。它存储在类中,而不是单个实例中。关于错误的评论只是一个旁注,这就是我把它放在括号中的原因。
    • 这更容易理解。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-13
    • 2021-08-17
    • 2010-12-27
    • 1970-01-01
    • 1970-01-01
    • 2015-12-07
    • 2023-02-02
    相关资源
    最近更新 更多