关于python:在__init__之外初始化字段

Initializing field outside __init__

我需要一些帮助来理解Python初始化是如何工作的。我有一个类(bar),另一个类(foo)作为字段/变量。当我试图在bar中直接初始化这个变量时(而不是在类uu init_uuu中),bar的所有实例都将指向相同的foo。但是如果我有一个uuu init_uuu方法,就像在bar2中一样,每个bar2实例都有一个唯一的foo实例。这里发生了什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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"
Bar2"

print first
print second
print first.foo
print second.foo

例如,输出为:

1
2
3
4
5
6
7
8
9
10
11
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实例。为什么?

编辑:修正错误,在第一个.foo中打印两次。结果行为仍然如输出中所示。


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

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

1
2
3
4
5
def foo():
  class A: pass
  a = A()
  a.msg ="Hello"
  return a

因为python是duck类型的(如果它像duck一样嘎嘎叫,看起来像duck,那么它就是duck类型的),函数foo的用户甚至不需要知道该类的名称,他们只需要知道foo返回一个带有member msg的对象。您可以这样使用它:

1
2
a = foo()
print a.msg

所以在您的示例中,当执行bar的定义时,将运行classes语句,包括创建foo对象。当执行bar2的定义时,classes语句在其中运行一个名为init的函数的定义。当一个对象被创建时(在调用另一个函数__new__之后,但这不在点之外),python使用它作为要调用的函数的名称。

因此,类定义(类内的代码,其中bar创建foo对象)在引入类时只运行一次。每次制造一个新对象时,都会一次又一次地调用__init__,因此在bar2中,创建foo也会一次又一次地进行。

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


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

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


类也是对象,它们有自己的变量集。不仅如此,当Python在对象中找不到变量时,它会在类中查找是否可以在那里找到它,这就是它所使用的。因为类是在该类的所有对象之间共享的,所以该类中的变量也是共享的。


1
2
3
4
5
6
7
8
first = Bar()
second = Bar()

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

这里,您要打印两次first.foo,这就是为什么打印相同的foo对象。

1
2
3
4
5
6
7
8
9
first = Bar2()
second = Bar2()

print"
Bar2"

print first
print second
print first.foo
print second.foo

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

1
2
3
4
5
6
7
8
9
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的每个对象都是唯一的,除非另有说明。