Skip to content

Instantly share code, notes, and snippets.

@goerz
Created December 10, 2015 23:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save goerz/576f13e1a362bf5c6e3f to your computer and use it in GitHub Desktop.
Save goerz/576f13e1a362bf5c6e3f to your computer and use it in GitHub Desktop.
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