1
#! /usr/bin/python

class my_class:
    # 1. __init__
    def __init__(self):
        self.my_set = set()

    # 2. __init__
    #def __init__(self, arg_set = set()):
    #    self.my_set = arg_set 

c1 = my_class()
c1.my_set.add('a')

print c1.my_set

c2 = my_class()
c2.my_set.add('b')

print c1.my_set

my_class has 2 ways of defining __init__:

If I use 1st way, output is as expected:
set(['a'])
set(['a'])

If I use 2nd way, output is unexpected:
set(['a'])
set(['a', 'b'])

What is wrong with 2nd way? How can modification of C2 (a separate object), result in modification of c1?

Edit: Updated the question title to reflect specific area of concern

nishant80
  • 136
  • 1
  • 9
  • 8
    Related: http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument, I believe – TerryA Nov 06 '13 at 10:29
  • 1
    @Haidro. Thanks for the link. It has very good information on this behavior. For me the learning is - "Avoid default parameter value for mutable objects unless you know what you are doing" – nishant80 Nov 06 '13 at 10:44
  • 1
    Pretty much. Just be aware on the outputs, and check out [egor's](http://stackoverflow.com/a/19809778/1971805) answer on a great solution around it. It's very helpful – TerryA Nov 06 '13 at 10:45

2 Answers2

5

From http://docs.python.org/2/reference/compound_stmts.html#function-definitions

Default parameter values are evaluated when the function definition is executed. This means that the expression is evaluated once, when the function is defined, and that the same “pre-computed” value is used for each call. This is especially important to understand when a default parameter is a mutable object, such as a list or a dictionary: if the function modifies the object (e.g. by appending an item to a list), the default value is in effect modified.

Thats the reason why your second method appends values everytime.

Moreover, modify the second __init__ like this

def __init__(self, arg_set = set()):
   print id(arg_set)
   self.my_set = arg_set 

Now, when you run the code, you ll always get the same address (id function in CPython returns the address of the object in memory). So, default arguments are not created everytime the function is invoked, but when evaluated the first time.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
1

thefourtheye is completely right on the cause of this behavior. If you want to use the second version of your constructor, it should be like this:

def __init__(self, arg_set=None):
    if arg_set is None:
        self.my_set = set()
    else:
        self.my_set = arg_set
egor-k8n
  • 91
  • 3