Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

python

oop

class

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.

like image 448
int3 Avatar asked Apr 21 '10 08:04

int3


2 Answers

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.

like image 131
Xavier Ho Avatar answered Sep 25 '22 21:09

Xavier Ho


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 
like image 22
badp Avatar answered Sep 23 '22 21:09

badp