How should I declare default values for instance variables in Python?

Question:

Should I give my class members default values like this:

class Foo:
    num = 1

or like this?

class Foo:
    def __init__(self):
        self.num = 1

In this question I discovered that in both cases,

bar = Foo()
bar.num += 1

is a well-defined operation.

I understand that the first method will give me a class variable while the second one will not. However, if I do not require a class variable, but only need to set a default value for my instance variables, are both methods equally good? Or one of them more ‘pythonic’ than the other?

One thing I’ve noticed is that in the Django tutorial, they use the second method to declare Models. Personally I think the second method is more elegant, but I’d like to know what the ‘standard’ way is.

Asked By: int3

||

Answers:

The two snippets do different things, so it’s not a matter of taste but a matter of what’s the right behaviour in your context. Python documentation explains the difference, but here are some examples:

Exhibit A

class Foo:
  def __init__(self):
    self.num = 1

This binds num to the Foo instances. Change to this field is not propagated to other instances.

Thus:

>>> foo1 = Foo()
>>> foo2 = Foo()
>>> foo1.num = 2
>>> foo2.num
1

Exhibit B

class Bar:
  num = 1

This binds num to the Bar class. Changes are propagated!

>>> bar1 = Bar()
>>> bar2 = Bar()
>>> bar1.num = 2 #this creates an INSTANCE variable that HIDES the propagation
>>> bar2.num
1
>>> Bar.num = 3
>>> bar2.num
3
>>> bar1.num
2
>>> bar1.__class__.num
3

Actual answer

If I do not require a class variable, but only need to set a default value for my instance variables, are both methods equally good? Or one of them more ‘pythonic’ than the other?

The code in exhibit B is plain wrong for this: why would you want to bind a class attribute (default value on instance creation) to the single instance?

The code in exhibit A is okay.

If you want to give defaults for instance variables in your constructor I would however do this:

class Foo:
  def __init__(self, num = None):
    self.num = num if num is not None else 1

…or even:

class Foo:
  DEFAULT_NUM = 1
  def __init__(self, num = None):
    self.num = num if num is not None else DEFAULT_NUM

…or even: (preferrable, but if and only if you are dealing with immutable types!)

class Foo:
  def __init__(self, num = 1):
    self.num = num

This way you can do:

foo1 = Foo(4)
foo2 = Foo() #use default
Answered By: badp

Using class members for default values of instance variables is not a good idea, and it’s the first time I’ve seen this idea mentioned at all. It works in your example, but it may fail in a lot of cases. E.g., if the value is mutable, mutating it on an unmodified instance will alter the default:

>>> class c:
...     l = []
... 
>>> x = c()
>>> y = c()
>>> x.l
[]
>>> y.l
[]
>>> x.l.append(10)
>>> y.l
[10]
>>> c.l
[10]
Answered By: Max Shawabkeh

Using class members to give default values works very well just so long as you are careful only to do it with immutable values. If you try to do it with a list or a dict that would be pretty deadly. It also works where the instance attribute is a reference to a class just so long as the default value is None.

I’ve seen this technique used very successfully in repoze which is a framework that runs on top of Zope. The advantage here is not just that when your class is persisted to the database only the non-default attributes need to be saved, but also when you need to add a new field into the schema all the existing objects see the new field with its default value without any need to actually change the stored data.

I find it also works well in more general coding, but it’s a style thing. Use whatever you are happiest with.

Answered By: Duncan

Extending bp’s answer, I wanted to show you what he meant by immutable types.

First, this is okay:

>>> class TestB():
...     def __init__(self, attr=1):
...         self.attr = attr
...     
>>> a = TestB()
>>> b = TestB()
>>> a.attr = 2
>>> a.attr
2
>>> b.attr
1

However, this only works for immutable (unchangable) types. If the default value was mutable (meaning it can be replaced), this would happen instead:

>>> class Test():
...     def __init__(self, attr=[]):
...         self.attr = attr
...     
>>> a = Test()
>>> b = Test()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[1]
>>> 

Note that both a and b have a shared attribute. This is often unwanted.

This is the Pythonic way of defining default values for instance variables, when the type is mutable:

>>> class TestC():
...     def __init__(self, attr=None):
...         if attr is None:
...             attr = []
...         self.attr = attr
...     
>>> a = TestC()
>>> b = TestC()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[]

The reason my first snippet of code works is because, with immutable types, Python creates a new instance of it whenever you want one. If you needed to add 1 to 1, Python makes a new 2 for you, because the old 1 cannot be changed. The reason is mostly for hashing, I believe.

Answered By: Xavier Ho

You can also declare class variables as None which will prevent propagation. This is useful when you need a well defined class and want to prevent AttributeErrors.
For example:

>>> class TestClass(object):
...     t = None
... 
>>> test = TestClass()
>>> test.t
>>> test2 = TestClass()
>>> test.t = 'test'
>>> test.t
'test'
>>> test2.t
>>>

Also if you need defaults:

>>> class TestClassDefaults(object):
...    t = None
...    def __init__(self, t=None):
...       self.t = t
... 
>>> test = TestClassDefaults()
>>> test.t
>>> test2 = TestClassDefaults([])
>>> test2.t
[]
>>> test.t
>>>

Of course still follow the info in the other answers about using mutable vs immutable types as the default in __init__.

Answered By: Realistic

With dataclasses, a feature added in Python 3.7, there is now yet another (quite convenient) way to achieve setting default values on class instances. The decorator dataclass will automatically generate a few methods on your class, such as the constructor. As the documentation linked above notes, "[t]he member variables to use in these generated methods are defined using PEP 526 type annotations".

Considering OP’s example, we could implement it like this:

from dataclasses import dataclass

@dataclass
class Foo:
    num: int = 0

When constructing an object of this class’s type we could optionally overwrite the value.

print('Default val: {}'.format(Foo()))
# Default val: Foo(num=0)
print('Custom val: {}'.format(Foo(num=5)))
# Custom val: Foo(num=5)
Answered By: Johannes Gehrs
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.