Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Example code that illustrates the (possibly surprising) behavior of Python instance and class attributes
class MyNewClass(object):
a = 1 # class attribute
def __init__(self):
self.b = 1 # instance attribute
def get_a_via_instance(self):
return self.a
def get_a_via_class(self):
return self.__class__.a # = MyNewClass.a
class MyOldClass():
# This is an old style class (Python 2 only)
a = 1 # class attribute
def __init__(self):
self.b = 1 # instance attribute
def get_a_via_instance(self):
return self.a
def get_a_via_class(self):
return self.__class__.a # = MyOldClass.a
class MySlotClass(object):
__slots__ = ['b', ]
a = 1 # class attribute
def __init__(self):
self.b = 1 # instance attribute
def get_a_via_instance(self):
return self.a
def get_a_via_class(self):
return self.__class__.a # = MySlotClass.a
def assert_dict(inst, attrs):
try:
assert sorted(list(inst.__dict__)) == sorted(attrs)
except AttributeError:
print(str(inst.__class__)+" has no __dict__")
def test_class_behavior():
for cls in [MyNewClass, MyOldClass, MySlotClass]:
inst1 = cls()
inst2 = cls()
# all instances share the same class attribute
assert inst1.a == inst2.a == 1
assert inst1.get_a_via_instance() == inst1.get_a_via_class()
cls.a = 2
assert inst1.a == inst2.a == 2
# the class attribute is *not* part of the instance __dict__, only the
# instance attribute are!
assert_dict(inst1, ['b', ])
assert_dict(inst2, ['b', ])
# we can create arbitrary new instance attributes, which are added to
# the instance __dict__
try:
inst1.new_attr = 1
assert_dict(inst1, ['b', 'new_attr'])
except AttributeError as exc:
# ... except if the class has __slots__
print("%s: %s" % (str(cls), str(exc)))
# Assigning to the class attribute name of an instance does *not* set
# the value of the class attribute, but creates a new instance
# attribute that shadows the class attribute
try:
inst1.a = 1
assert inst1.a == inst1.get_a_via_instance() == 1
assert inst2.a == 2
assert inst1.get_a_via_class() == inst2.a
assert_dict(inst1, ['a', 'b', 'new_attr'])
assert_dict(inst2, ['b', ])
except AttributeError as exc:
# ... except if the class has __slots__
print("%s: %s" % (str(cls), str(exc)))
# One could actually remove the shadowing instance attribute, again
# allowing access to the class attribute
try:
del inst1.a
assert_dict(inst1, ['b', 'new_attr'])
except AttributeError as exc:
print("%s: %s" % (str(cls), str(exc)))
assert inst1.a == 2
if __name__ == "__main__":
test_class_behavior()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.