Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Class Members Initialization

I have just recently battled a bug in Python. It was one of those silly newbie bugs, but it got me thinking about the mechanisms of Python (I'm a long time C++ programmer, new to Python). I will lay out the buggy code and explain what I did to fix it, and then I have a couple of questions...

The scenario: I have a class called A, that has a dictionary data member, following is its code (this is simplification of course):

class A:     dict1={}      def add_stuff_to_1(self, k, v):         self.dict1[k]=v      def print_stuff(self):         print(self.dict1) 

The class using this code is class B:

class B:      def do_something_with_a1(self):         a_instance = A()         a_instance.print_stuff()                 a_instance.add_stuff_to_1('a', 1)         a_instance.add_stuff_to_1('b', 2)             a_instance.print_stuff()      def do_something_with_a2(self):         a_instance = A()             a_instance.print_stuff()                     a_instance.add_stuff_to_1('c', 1)         a_instance.add_stuff_to_1('d', 2)             a_instance.print_stuff()      def do_something_with_a3(self):         a_instance = A()             a_instance.print_stuff()                     a_instance.add_stuff_to_1('e', 1)         a_instance.add_stuff_to_1('f', 2)             a_instance.print_stuff()      def __init__(self):         self.do_something_with_a1()         print("---")         self.do_something_with_a2()         print("---")         self.do_something_with_a3() 

Notice that every call to do_something_with_aX() initializes a new "clean" instance of class A, and prints the dictionary before and after the addition.

The bug (in case you haven't figured it out yet):

>>> b_instance = B() {} {'a': 1, 'b': 2} --- {'a': 1, 'b': 2} {'a': 1, 'c': 1, 'b': 2, 'd': 2} --- {'a': 1, 'c': 1, 'b': 2, 'd': 2} {'a': 1, 'c': 1, 'b': 2, 'e': 1, 'd': 2, 'f': 2} 

In the second initialization of class A, the dictionaries are not empty, but start with the contents of the last initialization, and so forth. I expected them to start "fresh".

What solves this "bug" is obviously adding:

self.dict1 = {} 

In the __init__ constructor of class A. However, that made me wonder:

  1. What is the meaning of the "dict1 = {}" initialization at the point of dict1's declaration (first line in class A)? It is meaningless?
  2. What's the mechanism of instantiation that causes copying the reference from the last initialization?
  3. If I add "self.dict1 = {}" in the constructor (or any other data member), how does it not affect the dictionary member of previously initialized instances?

EDIT: Following the answers I now understand that by declaring a data member and not referring to it in the __init__ or somewhere else as self.dict1, I'm practically defining what's called in C++/Java a static data member. By calling it self.dict1 I'm making it "instance-bound".

like image 796
Roee Adler Avatar asked May 15 '09 06:05

Roee Adler


2 Answers

What you keep referring to as a bug is the documented, standard behavior of Python classes.

Declaring a dict outside of __init__ as you initially did is declaring a class-level variable. It is only created once at first, whenever you create new objects it will reuse this same dict. To create instance variables, you declare them with self in __init__; its as simple as that.

like image 123
Paolo Bergantino Avatar answered Sep 23 '22 13:09

Paolo Bergantino


When you access attribute of instance, say, self.foo, python will first find 'foo' in self.__dict__. If not found, python will find 'foo' in TheClass.__dict__

In your case, dict1 is of class A, not instance.

like image 34
kcwu Avatar answered Sep 21 '22 13:09

kcwu