理解Python类属性

5

我是一个编程新手,刚开始学习Python。这可能是一个非常愚蠢的问题,请原谅我的无知。 考虑以下代码片段:

class Test1:
    bar = 10    
    def display(self,foo):
        self.foo=foo
        print "foo : ",self.foo #80
    def display1(self):
        print "bar: ", self.bar #10
        print "again foo: ", self.foo #80

if __name__ == '__main__':
    test1 = Test1()
    test1.display(80)
    test1.display1()
    print test1.bar #10
    print test1.foo #80

我希望了解在使用foo和bar(就它们所定义的范围而言)时有什么区别,因为从作用域的角度来看,它们都可以在所有地方相互访问,唯一的区别是一个在函数内部,另一个在类内部,但它们两者仍然是“实例”变量。那么哪个是好的习惯呢?
此外,如果我稍微修改显示函数如下:
    def display(self,foo):
        self.foo=foo
        foo = foo
        print "self.foo : ",self.foo 
        print "foo : ",foo 

请问有人能解释一下Python如何看待这个问题,即这两个foo之间的self关键字带来了什么不同/意义。


4
不,bar不是实例变量。这两者非常不同,这就是为什么这不是关于“良好实践”的问题。这取决于你的情况,因为它们有不同的用途。 - phant0m
1
同时,self.bar也是可行的,因为首先会在实例的命名空间中搜索名称bar,然后再在类中搜索。self.bar可行并不总是意味着bar是一个实例变量。 - ersran9
3个回答

4

bar是一个类属性,而foo是一个实例属性。主要区别在于,所有类的实例都可以使用bar,而只有在调用该实例上的display时,foo才可用。

>>> ins1 = Test1()

ins1.bar 能正常工作是因为它是一个类属性,被所有实例共享。

>>> ins1.bar
10

但是在这里您无法直接访问foo,因为它尚未被定义:

>>> ins1.foo
Traceback (most recent call last):
  File "<ipython-input-62-9495b4da308f>", line 1, in <module>
    ins1.foo
AttributeError: Test1 instance has no attribute 'foo'

>>> ins1.display(12)
foo :  12
>>> ins1.foo
12

如果你想在实例被创建时初始化一些实例属性,请将它们放在__init__方法内。

class A(object):
    bar =  10
    def __init__(self, foo):
        self.foo = foo   #this gets initialized when the instance is created
    def func(self, x):
        self.spam = x    #this will be available only when you call func() on the instance
...         
>>> a = A(10)
>>> a.bar
10
>>> a.foo
10
>>> a.spam
Traceback (most recent call last):
  File "<ipython-input-85-3b4ed07da1b4>", line 1, in <module>
    a.spam
AttributeError: 'A' object has no attribute 'spam'

>>> a.func(2)
>>> a.spam
2

1
我认为主要的区别在于类属性是“共享”的,而不是可用于所有实例。 - phant0m

3
bar是一个类属性。由于Python中的类也是对象,它们也可以有属性。bar恰好存在于Test对象上,而不是实例本身。
由于Python解析属性查找的方式,看起来test1有一个bar属性,但实际上并没有。
另一方面,在调用display(80)后,foo存在于实例test1中。这意味着Test的不同实例可以在各自的foo属性中具有不同的值。
当然,您可以使用类变量作为某种“共享默认值”,然后使用实例属性进行“覆盖”,但这可能会让人感到困惑。 第二个问题
def display(self,foo):
    self.foo=foo
    foo = foo
    print "self.foo : ",self.foo 
    print "foo : ",foo 

让我们先澄清一个细节: self 不是关键字,它只是惯例,将第一个参数称为 "self",如果您喜欢,也可以称之为 "this"、"that" 或 "bar",但我不建议这样做。

Python 将调用方法的对象作为第一个参数传递。

def display(self,foo): 

这个 foo 是 display 实例函数的第一个参数的名称。

    self.foo=foo

这将把你在display()调用的实例中名称为“foo”的属性设置为你作为第一个参数传递的值。使用你的例子test1.display(80)self将是test1foo80,因此test1.foo将被设置为80

    foo = foo

这段代码什么也没做,它引用了第一个参数foo

接下来的两行代码再次引用了实例变量foo和第一个参数foo


1
个人而言,我不喜欢在方法中定义实例变量,除了在__init__中或者像你上面的示例中定义为类变量bar
同样地,我喜欢通过查看顶部来了解类的每个成员。无论是将类变量用作实例变量(我通常不这样做),还是在__init__中定义实例变量,检查类定义的第一部分就可以轻松确定哪些内容被定义了。
如果您不需要将变量作为类成员访问(即只在那里定义它以避免在__init__方法中编写self.variable = val),那么我会避免使用它。如果您可能需要将其作为类变量访问,则按照您使用bar的方式是可以的。
这是我编写类的首选方式。
class Test1:

    def __init__(self):
        self.foo = None
        self.bar = 10

    def display(self,foo):
        self.foo=foo
        print "foo : ",self.foo #80

    def display1(self):
        print "bar: ", self.bar #10
        print "again foo: ", self.foo #80

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接