views:

96

answers:

5

I've seen code that looks something like this:

class MyClass:
    def __init__(self, someargs):
        myObj = OtherClass()
        myDict = {}
        ...code to setup myObj, myDict...
        self.myObj = myObj
        self.myDict = myDict

My first thought when I saw this was: Why not just use self.myObj and self.myDict in the beginning? It seems inefficient to construct local objects, then assign them to the members. The code to construct the objects do possibly throw exceptions, maybe they did this so it wouldn't leave a half-constructed object? Do you do this, or just construct the members directly?

+1  A: 

I'm not sure I fully understand your question, but keep in mind that assigning self.myObj = myObj will simply assign a reference so it isn't likely to slow things down much. My guess is that idiom is being used to save the programmer from typing the word self a bunch of times.

easel
+3  A: 

If you are worried about the performance implication of copying an object reference, you are probably using the wrong language :)

Do what's more readable to you. In this case, it depends on how long is the init method.

Marco Mariani
I'm only worried in the sense of avoiding premature pessimization :)
Colin
"what you can read, you can later optimize" - me
Marco Mariani
+8  A: 

It's faster and more readable to construct the object and then attach it to self.

class Test1(object):
    def __init__(self):
        d = {}
        d['a'] = 1
        d['b'] = 2
        d['c'] = 3
        self.d = d

class Test2(object):
    def __init__(self):
        self.d = {}
        self.d['a'] = 1
        self.d['b'] = 2
        self.d['c'] = 3

import dis
print "Test1.__init__"
dis.dis(Test1.__init__)

print "Test2.__init__"
dis.dis(Test2.__init__)

disassemles to:

Test1.__init__
  4           0 BUILD_MAP                0
              3 STORE_FAST               1 (d)

  5           6 LOAD_CONST               1 (1)
              9 LOAD_FAST                1 (d)
             12 LOAD_CONST               2 ('a')
             15 STORE_SUBSCR        

  6          16 LOAD_CONST               3 (2)
             19 LOAD_FAST                1 (d)
             22 LOAD_CONST               4 ('b')
             25 STORE_SUBSCR        

  7          26 LOAD_CONST               5 (3)
             29 LOAD_FAST                1 (d)
             32 LOAD_CONST               6 ('c')
             35 STORE_SUBSCR        

  8          36 LOAD_FAST                1 (d)
             39 LOAD_FAST                0 (self)
             42 STORE_ATTR               0 (d)
             45 LOAD_CONST               0 (None)
             48 RETURN_VALUE        
Test2.__init__
 12           0 BUILD_MAP                0
              3 LOAD_FAST                0 (self)
              6 STORE_ATTR               0 (d)

 13           9 LOAD_CONST               1 (1)
             12 LOAD_FAST                0 (self)
             15 LOAD_ATTR                0 (d)
             18 LOAD_CONST               2 ('a')
             21 STORE_SUBSCR        

 14          22 LOAD_CONST               3 (2)
             25 LOAD_FAST                0 (self)
             28 LOAD_ATTR                0 (d)
             31 LOAD_CONST               4 ('b')
             34 STORE_SUBSCR        

 15          35 LOAD_CONST               5 (3)
             38 LOAD_FAST                0 (self)
             41 LOAD_ATTR                0 (d)
             44 LOAD_CONST               6 ('c')
             47 STORE_SUBSCR        
             48 LOAD_CONST               0 (None)
             51 RETURN_VALUE

You can see that STORE_ATTR only gets called once doing it the first way at the end. Doing it the other way, STORE_ATTR still gets called right at the beginning but now LOAD_ATTR gets called for every access to the dictionary. The more assignments there are, the higher the cost. Every other instruction is the same. It's still a ridiculously small cost.

This trick can be exploited to make loops with many iterations run faster. It's not uncommon to see things like

foo = self.foo
factorial = math.factorial
for x in really_big_iterator:
    foo(factorial(x))

another trick is to pass global functions as default arguments to a function that has a loop like that or gets called a whole bunch to save some attribute lookups: it's in the local scope which is the first one looked in.

def fast(iterators, sum=sum):
    for i in iterator:
        yield sum(i)

now sum is right in the local scope.

aaronasterling
A: 

There is really no significant difference either way. One reason to create a local variable is if you are going to use the variable in _init_ a lot, you don't have to repeat the self. so much. e.g.

def __init__(self, someargs):
    self.myObj = OtherClass()
    self.myDict = {}
    self.myDict[1] = self.myObj
    self.myDict[2] = self.myObj
    self.myDict[3] = self.myObj
    self.myDict[4] = self.myObj
    self.myObj = myObj
    self.myDict = myDict

v.s.

def __init__(self, someargs):
    obj = OtherClass()
    d = {}
    d[1] = obj
    d[2] = obj
    d[3] = obj
    d[4] = obj
    self.myObj = obj
    self.myDict = d

I won't worry performance so much unless you have a good reason to.

Wai Yip Tung
A: 

assign reference don't take too much time, but type 'self' each time do take you time.

Hiber