Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between defining a member in __init__ to defining it in the class body in python?

Tags:

python

class

init

What is the difference between doing

class a:
   def __init__(self):
       self.val=1

to doing

class a:
   val=1
   def __init__(self):
       pass
like image 488
olamundo Avatar asked Oct 11 '09 03:10

olamundo


3 Answers

class a:
   def __init__(self):
       self.val=1

this creates a class (in Py2, a cruddy, legacy, old-style, don't do that! class; in Py3, the nasty old legacy classes have finally gone away so this would be a class of the one and only kind -- the **good* kind, which requires class a(object): in Py2) such that each instance starts out with its own reference to the integer object 1.

class a:
   val=1
   def __init__(self):
       pass

this creates a class (of the same kind) which itself has a reference to the integer object 1 (its instances start out with no per-instance reference).

For immutables like int values, it's hard to see a practical difference. For example, in either case, if you later do self.val = 2 on one instance of a, this will make an instance reference (the existing answer is badly wrong in this respect).

The distinction is important for mutable objects, because they have mutator methods, so it's pretty crucial to know if a certain list is unique per-instance or shared among all instances. But for immutable objects, since you can never change the object itself but only assign (e.g. to self.val, which will always make a per-instance reference), it's pretty minor.

Just about the only relevant difference for immutables: if you later assign a.val = 3, in the first case this will affect what's seen as self.val by each instance (except for instances that had their own self.val assigned to, or equivalent actions); in the second case, it will not affect what's seen as self.val by any instance (except for instances for which you had performed del self.val or equivalent actions).

like image 127
Alex Martelli Avatar answered Oct 26 '22 22:10

Alex Martelli


Others have explained the technical differences. I'll try to explain why you might want to use class variables.

If you're only instantiating the class once, then class variables effectively are instance variables. However, if you're making many copies, or want to share state among a few instances, then class variables are very handy. For example:

class Foo(object):
    def __init__(self):
        self.bar = expensivefunction()

myobjs = [Foo() for _ in range(1000000)]

will cause expensivefunction() to be called a million times. If it's going to return the same value each time, say fetching a configuration parameter from a database, then you should consider moving it into the class definition so that it's only called once and then shared across all instances.

I also use class variables a lot when memoizing results. Example:

class Foo(object):
    bazcache = {}

    @classmethod
    def baz(cls, key):
        try:
            result = cls.bazcache[key]
        except KeyError:
            result = expensivefunction(key)
            cls.bazcache[key] = result
        return result

In this case, baz is a class method; its result doesn't depend on any instance variables. That means we can keep one copy of the results cache in the class variable, so that 1) you don't store the same results multiple times, and 2) each instance can benefit from results that were cached from other instances.

To illustrate, suppose that you have a million instances, each operating on the results of a Google search. You'd probably much prefer that all those objects share those results than to have each one execute the search and wait for the answer.

So I'd disagree with Lennart here. Class variables are very convenient in certain cases. When they're the right tool for the job, don't hesitate to use them.

like image 40
Kirk Strauser Avatar answered Oct 26 '22 23:10

Kirk Strauser


As mentioned by others, in one case it's an attribute on the class on the other an attribute on the instance. Does this matter? Yes, in one case it does. As Alex said, if the value is mutable. The best explanation is code, so I'll add some code to show it (that's all this answer does, really):

First a class defining two instance attributes.

>>> class A(object):
...     def __init__(self):
...         self.number = 45
...         self.letters = ['a', 'b', 'c']
... 

And then a class defining two class attributes.

>>> class B(object):
...     number = 45
...     letters = ['a', 'b', 'c']
... 

Now we use them:

>>> a1 = A()
>>> a2 = A()
>>> a2.number = 15
>>> a2.letters.append('z')

And all is well:

>>> a1.number
45
>>> a1.letters
['a', 'b', 'c']

Now use the class attribute variation:

>>> b1 = B()
>>> b2 = B()
>>> b2.number = 15
>>> b2.letters.append('z')

And all is...well...

>>> b1.number
45
>>> b1.letters
['a', 'b', 'c', 'z']

Yeah, notice that when you changed, the mutable class attribute it changed for all classes. That's usually not what you want.

If you are using the ZODB, you use a lot of class attributes because it's a handy way of upgrading existing objects with new attributes, or adding information on a class level that doesn't get persisted. Otherwise you can pretty much ignore them.

like image 23
Lennart Regebro Avatar answered Oct 26 '22 22:10

Lennart Regebro