Skip to content

Instantly share code, notes, and snippets.

@benspaulding
Last active April 28, 2017 16:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benspaulding/af3feac287275618303132dbf84f5451 to your computer and use it in GitHub Desktop.
Save benspaulding/af3feac287275618303132dbf84f5451 to your computer and use it in GitHub Desktop.
Python class vs instance attributes
>>> class Foo(object):
... # Immutable objects are fine as class attrs.
... mystr = 'a'
... myint = 1
... mytpl = (1,)
...
... # But mutable objects often surprise you.
... mylst = []
... mydct = {}
>>> foo = Foo()
>>> foo.__dict__
{}
>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'Foo' objects>,
'mydct': {},
'myint': 1,
'mylst': [],
'mystr': 'a',
'mytpl': (1,)})
>>> # Here we simply make foo.mystr *point* to a new object 'b'.
>>> foo.mystr = 'b'
>>> foo.__dict__
{'mystr': 'b'}
>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'Foo' objects>,
'mydct': {},
'myint': 1,
'mylst': [],
'mystr': 'a',
'mytpl': (1,)})
>>> # But here we are actually mutating the object that foo.mylst already points too.
>>> foo.mylst.append(2)
>>> foo.__dict__
{'mystr': 'b'}
>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'Foo' objects>,
'mydct': {},
'myint': 1,
'mylst': [2],
'mystr': 'a',
'mytpl': (1,)})
>>> foo.mylst is Foo.mylst
True
>>> # But here we set it to a new object.
>>> foo.mylst = ['A new list!']
>>> foo.mylst is Foo.mylst
False
>>> foo.__dict__
{'mylst': ['A new list!'], 'mystr': 'b'}
>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>,
'__doc__': None,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'Foo' objects>,
'mydct': {},
'myint': 1,
'mylst': [2],
'mystr': 'a',
'mytpl': (1,)})
>>> # So you need to set the instance attribute first. The best way to do that
>>> # is in the __init__.
>>> import copy
>>> class Bar(Foo)
... def __init__(self, mylst=None, mydct=None, myset=None):
... # If you really want to set a default on the class, make a copy of it.
... if mylst is None:
... self.mylst = copy.deepcopy(self.mylst)
... if mydct is None:
... self.mydct = copy.deepcopy(self.mydct)
...
... # But the more common practice is to just put your default here
... # rather than on the class (and certainly *not* in the function kwargs!)
... if myset is None:
... self.myset = set()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment