类变量,instance.var和instance .__ class __。var之间的区别

时间:2018-05-12 22:07:58

标签: python python-3.x class

我看到这被标记为"类和实例变量之间的区别是什么?"但是我不相信我在我的示例中使用了任何实例变量,我的类都没有__init__我以两种不同的方式编辑类变量并尝试理解差异在它们之间,类和实例变量之间的区别。

我试图了解仅使用.var.__class__.var调用类变量之间的区别。我认为这与子类化有关,所以我编写了以下代码。

class Foo:
    foo = 0

class Bar(Foo):
    bar = 1

def print_class_vals(f, b):
    """ prints both instant.var and instant.__class__.var for foo and bar"""
    print("f.foo: {}, {} "
          "b.foo: {}, {} "
          "b.bar: {}, {} "
          "".format(f.foo, f.__class__.foo,
                    b.foo, b.__class__.foo,
                    b.bar, b.__class__.bar))

f = Foo()
b = Bar()
print_class_vals(f, b)

Foo.foo += 1
print_class_vals(f, b)

Bar.foo += 1
print_class_vals(f, b)

Bar.bar += 1
print_class_vals(f, b)

这输出以下内容:

f.foo: 0, 0, b.foo: 0, 0, b.bar: 1, 1 
f.foo: 1, 1, b.foo: 1, 1, b.bar: 1, 1 
f.foo: 1, 1, b.foo: 2, 2, b.bar: 1, 1 
f.foo: 1, 1, b.foo: 2, 2, b.bar: 2, 2 

我似乎无法在调用inst.varinst.__class__.var之间找到任何区别。它们有何不同,我何时应该使用另一个?

2 个答案:

答案 0 :(得分:2)

Python将首先在实例命名空间/ dict中查找名称(属性)。如果它没有找到,那么它将在类命名空间中查找。如果它仍然没有找到,那么它将遍历关于MRO的基类(方法解析顺序)。

您在那里所做的是定义类属性Bar.barclass Foo: foo = 1 f = Foo() f.foo = 2 print('f.foo = {!r}'.format(f.foo)) print('f.__class__.foo = {!r}'.format(f.__class__.foo)) 。 您从未修改过任何实例名称空间。

试试这个:

{{1}}

你将能够理解其中的差异。

答案 1 :(得分:2)

尽管Gabriel Reis's answer完美地解释了这种特殊情况,但实际上 <{1}}和f.foo 之间存在差异,即使f.__class__.foo isn& #39;被实例属性遮蔽。

比较

foo

>>> class Foo: ... foo = 1 ... def bar(self): pass ... baz = lambda self: None >>> f = Foo() >>> f.foo 1 >>> f.__class__.foo 1 >>> f.bar <bound method Foo.bar of <__main__.Foo object at 0x11948cb00>> >>> f.__class__.bar <function __main__.Foo.bar(self)> >>> f.bar() >>> f.__class__.bar() TypeError: bar() missing 1 required positional argument: 'self' 也是如此。

不同之处在于,通过直接访问f.baz,您可以围绕descriptor protocol进行最终操作,这就是制作方法f.__class__.foo和类似的事情有效。

如果您想了解完整的详细信息,请阅读链接的HOWTO,但简短版本的内容比Gabriel的回答更多:

  

Python将首先在实例命名空间/ dict中查找名称(属性)。如果它找不到,那么它将在类命名空间中查找。如果它仍然没有找到,那么它将遍历关于MRO的基类(方法解析顺序)。

但是如果它在类名称空间(或任何基类)中找到它,并且它找到的是描述符(具有@property方法的值),它会执行额外的步骤。细节取决于它是数据还是非数据描述符(基本上,它是否 还有__get__方法),但简短的版本是,而不是给你值,它在值上调用__set__并为您提供该值返回的值。函数有__get__方法,返回绑定方法;属性有__get__方法,可以调用属性get方法;等