【问题标题】:Class variables, difference between instance.var and instance.__class__.var类变量,instance.var 和 instance.__class__.var 的区别
【发布时间】:2018-05-13 06:32:52
【问题描述】:

我看到这被标记为“类变量和实例变量之间有什么区别?”的重复项。但是我不相信我在示例中使用了任何实例变量,我的两个类都没有__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 之间的任何区别。它们有什么不同,我什么时候应该使用其中一种?

【问题讨论】:

  • 你几乎不应该使用__class__
  • @chepner,副本的最佳答案最后有一个解释,讨论了为什么你最终得到相同的值。
  • 如果你没有一个实例变量来隐藏它,那么访问一个类变量绝对没有区别。
  • @MisterMiyagi 实际上,这不是真的。用一个方法(或者一个函数值的类属性,也是一样的)试试。

标签: python python-3.x class


【解决方案1】:

虽然Gabriel Reis's answer 完美地解释了这种特殊情况,但实际上f.foof.__class__.foo 之间存在差异即使foo 没有被实例属性遮蔽 /em>。

比较:

>>> 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 也是如此。

不同之处在于,通过直接访问 f.__class__.foo,您将绕过 descriptor protocol,这是使方法、@property 和类似的东西起作用的东西。

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

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

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

【讨论】:

    【解决方案2】:

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

    您在那里所做的是定义类属性Foo.fooBar.bar。 您从未修改过任何实例命名空间。

    试试这个:

    class Foo:
        foo = 1
    
    f = Foo()
    f.foo = 2
    
    print('f.foo = {!r}'.format(f.foo))
    print('f.__class__.foo = {!r}'.format(f.__class__.foo))
    

    你将能够理解其中的区别。

    【讨论】:

    • 这有点过于简单了,但我认为它对 OP 来说已经足够解释了,所以我投了赞成票,但写了另一个答案。
    猜你喜欢
    • 2010-11-06
    • 2017-07-12
    • 1970-01-01
    • 2014-03-13
    • 2018-08-02
    • 2012-11-28
    • 1970-01-01
    • 2021-05-07
    • 2011-09-04
    相关资源
    最近更新 更多