Я вижу, что это было помечено как дубликат "В чем разница между переменными класса и экземпляра?" Однако я не верю, что использую какие-либо переменные экземпляра в моем примере, ни один из моих классов не имеет __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.var и inst.__class__.var. Чем они отличаются и когда лучше использовать одно перед другим?
@chepner, В верхнем ответе на дубликат в конце есть объяснение, в котором обсуждается, почему вы получаете такое же значение.
Нет абсолютно никакой разницы в доступе к переменной класса, если у вас нет переменной экземпляра, которая ее затеняет.
@MisterMiyagi На самом деле это неправда. Попробуйте это с помощью метода (или атрибута класса, имеющего значение функции, что одно и то же).






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))
И вы сможете понять разницу.
Это немного упрощено, но я думаю, что это достаточно для OP, поэтому я поддержал его, но написал другой ответ.
Хотя Ответ Габриэля Рейса прекрасно объясняет эту конкретную ситуацию, на самом деле существует является разница между f.foo и f.__class__.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'
То же самое и с f.baz.
Разница в том, что, напрямую обращаясь к f.__class__.foo, вы завершаете работу вокруг протокол дескриптора, что и заставляет работать методы, @property и подобные вещи.
Если вы хотите получить полную информацию, прочитайте связанный HOWTO, но вкратце он говорит о том, что это немного больше, чем говорится в ответе Габриэля:
Python will look up for a name (attribute) first in the instance namespace/dict. If it doesn't find there, then it will look up in the class namespace. If it still doesn't find there, then it will walk through the base classes respecting the MRO (method resolution order).
Но если он находит его в пространстве имен класса (или любом базовом классе) и находит дескриптор (значение с помощью метода __get__), он выполняет дополнительный шаг. Детали зависят от того, является ли это дескриптор данных или нет (в основном, имеет ли он также метод __set__), но краткая версия заключается в том, что вместо того, чтобы дать вам значение, он вызывает __get__ для значения и дает вам это значение. возвращается. Функции имеют метод __get__, который возвращает связанный метод; у свойств есть метод __get__, который вызывает метод получения свойства; и т.п.
Практически никогда не следует использовать
__class__.