Python类的头部分中的可执行语句

Executable statements in the header part of a Python class

本问题已经有最佳答案,请猛点这里访问。

我试图创建一个类,其中包含从几个文件中读取的各种数据。通常的方法可能是定义一个构造函数(__init__)并在这个例程中读取数据,例如,

1
2
3
4
5
6
7
8
9
10
11
12
13
from SomeOtherMod import reader   # some data reader

class Myclass:

    def __init__( self ):
        self.N = reader.readN()
        self.data = reader.readdata()
        self.foo = self.data.foo()
        self.bar = self.data.bar()
        ...    # more data to read and associated properties follow

    def othefunc( self ):
        ...    # use self.N, self.data, ...

但似乎我也可以在类的头部分编写相同的东西,而不使用__init__,例如,

1
2
3
4
5
6
7
8
9
10
class Myclass:

    N = reader.readN()
    data = reader.readdata()
    foo = data.foo()
    bar = data.bar()
    ...

    def otherfunc( self ):
        ...

这看起来比第一个代码更简洁。所以我想知道第二个代码是否是定义Python类的各种字段的有效方法?这样做被认为是不好的做法,还是第一种方法和第二种方法有什么区别?我会感谢任何建议,因为我对Python还是个新手。多谢!


这两种方法有重要的区别。在某些用例中,您可以用任何一种方法来实现,但它们绝对不是等价的。

对于第一个方法,在实例化类的实例之前,不会执行__init__方法中的语句;也就是说,直到您的代码执行形式为a = MyClass()的语句。

第二种方法是在解释器第一次到达代码时执行类定义块内的语句。这可能在程序的开头(或附近),并且在导入包含类定义的模块时发生。这可能,也可能不适用于您的申请。

另外,请阅读juanpa.arrivilaga在注释中提供的链接,或者这个链接:为什么属性引用在Python继承中的作用是这样的?


是的-它们是非常不同的-当您在一个方法中分配给self.attribute时,您将其设置为实例属性-而当您在类体中分配给attribute时,您将创建一个类属性。类属性在所有实例之间共享。

在某些情况下,但并非总是如此,你可能会完全想要。您必须记住的是,如果yu更改了一个类属性,那么它将立即对该类的所有实例进行更改。

当然,python处理属性检索和赋值的方式有一种自然的表现,这使得类体中的赋值非常方便。如果有一个值应该是类的所有实例的默认值(例如,员工支出),但该值应该在对象生命周期的某个时间点进行自定义,则这是自然发生的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Employee:
    payout = 100

e1 = Employee()
e2 = Employee()

print(e1.payout, e2.payout)
# Payout is read from the class as it does not exist in any instance
# Python prints 100, 100

e1.payout = 120
# Now, an"payout" attribute is created in the"e1" instance
print(e1.payout, e2.payout)
# shows 120, 100

# And the outpt of the following lines will make
# really clear what is going on:
print(e1.__class__.__dict__, e2.__class__.__dict__)
# shows"{'payout': 100, ...}, {'payout': 100, ...}
print(e1.__dict__, e2.__dict__)
# shows"{'payout': 120}, {}


在第一种方法中,n、data、foo和bar是myclass实例的简单成员。每个MyClass对象都有自己的对象,更改一个对象对其他实例没有影响:

1
2
3
4
5
6
7
8
class X():
  def __init__(self):
    self.x = []

a = X()
b = X()
a.x.append(1)
a.x != b.x

在第二种方法中,它们是类的成员。您仍然可以通过实例访问它们,但它们将由所有实例共享。

1
2
3
4
5
6
7
class X():
  x = []

a = X()
b = X()
a.x.append(1)
a.x == b.x == [1]

两者都是有效的代码,但它们有不同的用途