Python 类变量,instance.var和instance.var之间的差异

Python 类变量,instance.var和instance.var之间的差异,python,python-3.x,class,Python,Python 3.x,Class,我看到这被标记为“类变量和实例变量之间的区别是什么?”的副本,但是我不相信我在我的示例中使用了任何实例变量,我的两个类都没有\uuuuu init\uuuuu我正在以两种不同的方式编辑类变量并试图理解它们之间的区别,不是类和实例变量之间的差异 我试图理解仅使用.var调用类变量与使用.class\uu.var调用类变量之间的区别。我认为这与子类化有关,所以我编写了以下代码 class Foo: foo = 0 class Bar(Foo): bar = 1 def print

我看到这被标记为“类变量和实例变量之间的区别是什么?”的副本,但是我不相信我在我的示例中使用了任何实例变量,我的两个类都没有
\uuuuu init\uuuuu
我正在以两种不同的方式编辑类变量并试图理解它们之间的区别,不是类和实例变量之间的差异

我试图理解仅使用
.var
调用类变量与使用
.class\uu.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.var
inst.\uuu class\uuuu.var
之间的任何区别。它们有何不同?何时应该使用一个而不是另一个?

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

您在这里所做的是定义类属性
Foo.Foo
Bar.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))

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

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

您在这里所做的是定义类属性
Foo.Foo
Bar.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))
您将能够理解其中的差异。

虽然完美地解释了这一特殊情况,但是
f.foo
f.\uu类\uuuuuuuuuuuuuuuuo
之间实际上存在差异,即使
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'
>>类Foo:
...     foo=1
...     def bar(自我):通过
...     baz=lambda self:无
>>>f=Foo()
>>>福福
1.
>>>f.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
1.
>>>酒吧
>>>f.。uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
>>>f.bar()
>>>f.。uuuu类uuuuu.bar()
TypeError:bar()缺少1个必需的位置参数:“self”
对于
f.baz
,情况也是如此

不同之处在于,通过直接访问
f.\uuuu class\uuuuu.foo
,您正在围绕进行一次结束运行,这是使方法、
@property
和类似的事情工作的原因

如果你想了解完整的细节,请阅读链接的HOWTO,但简短的版本是,它比Gabriel的回答要多一些:


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

但是,如果它在类名称空间(或任何基类)中找到它,并且它找到的是一个描述符(一个带有
\uuuuu get\uuuu
方法的值),它将执行额外的步骤。细节取决于它是数据描述符还是非数据描述符(基本上,它是否也有
\uuuuuu set\uuuuu
方法),但简短的版本是,它不给你值,而是对值调用
\uuuu get\uuuu
,并给出该值返回的值。函数有一个
\uuuuu get\uuuu
方法,该方法返回一个绑定方法;属性有一个调用属性get方法的
\uuuuuuuuuuuuuuuuuuuuuuu
方法;等。

虽然完美地解释了这种特殊情况,但实际上
f.foo
f.\uuuu class\uuuuuu.foo
之间存在差异,即使
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'
>>类Foo:
...     foo=1
...     def bar(自我):通过
...     baz=lambda self:无
>>>f=Foo()
>>>福福
1.
>>>f.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
1.
>>>酒吧
>>>f.。uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
>>>f.bar()
>>>f.。uuuu类uuuuu.bar()
TypeError:bar()缺少1个必需的位置参数:“self”
对于
f.baz
,情况也是如此

不同之处在于,通过直接访问
f.\uuuu class\uuuuu.foo
,您正在围绕进行一次结束运行,这是使方法、
@property
和类似的事情工作的原因

如果你想了解完整的细节,请阅读链接的HOWTO,但简短的版本是,它比Gabriel的回答要多一些:


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


但是,如果它在类名称空间(或任何基类)中找到它,并且它找到的是一个描述符(一个带有
\uuuuu get\uuuu
方法的值),它将执行额外的步骤。细节取决于它是数据描述符还是非数据描述符(基本上,它是否也有
\uuuuuu set\uuuuu
方法),但简短的版本是,它不给你值,而是对值调用
\uuuu get\uuuu
,并给出该值返回的值。函数有一个
\uuuuu get\uuuu
方法,该方法返回一个绑定方法;属性有一个调用属性get方法的
\uuuuuuuuuuuuuuuuuuuuuuu
方法;等等。

你不应该使用
\uuu class\uu
@chepner,副本顶部的答案在最后有一个解释,讨论了为什么你会得到相同的值。如果你没有一个实例变量来隐藏类变量,那么访问类变量是绝对没有区别的。@mistermiagi实际上,这不是真的。用一个方法(或者一个函数值的类属性,这是一样的)试试看