Построение объектов в __init__ - PullRequest
2 голосов
/ 31 августа 2010

Я видел код, который выглядит примерно так:

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

Моя первая мысль, когда я увидел это, была: Почему бы просто не использовать self.myObj и self.myDict в начале?Кажется неэффективным создавать локальные объекты, а затем назначать их членам.Код для конструирования объектов, возможно, генерирует исключения, может быть, они сделали это, чтобы он не оставил полу-построенный объект?Вы делаете это или просто создаете членов напрямую?

Ответы [ 5 ]

8 голосов
/ 31 августа 2010

Это быстрее и более удобочитаемым для создания объекта, а затем присоединить его к 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__)

разбирает на:

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

Вы можетевидите, что STORE_ATTR вызывается только один раз, когда он делает это первым способом в конце.Делая это иначе, STORE_ATTR по-прежнему вызывается в самом начале, но теперь LOAD_ATTR вызывается при каждом доступе к словарю.Чем больше заданий, тем выше стоимость.Все остальные инструкции одинаковы.Это все еще смехотворно малая стоимость.

Этот трюк можно использовать для ускорения циклов со многими итерациями.Нередко можно увидеть такие вещи, как

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

Еще один прием - передать глобальные функции в качестве аргументов по умолчанию для функции, которая имеет такой цикл или вызывается целой связкой для сохранения поиска некоторых атрибутов: он находится влокальная область видимости, которая является первой из просмотренных.

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

теперь сумма верна в локальной области видимости.

3 голосов
/ 31 августа 2010

Если вы беспокоитесь о производительности, связанной с копированием ссылки на объект, вы, вероятно, используете не тот язык:)

Делайте то, что вам удобнее читать.В этом случае это зависит от того, сколько времени занимает метод init.

1 голос
/ 31 августа 2010

Я не уверен, что полностью понимаю ваш вопрос, но имейте в виду, что назначение self.myObj = myObj просто назначит ссылку, поэтому вряд ли это сильно замедлит процесс. Я предполагаю, что идиома используется, чтобы спасти программиста от набора слова self несколько раз.

0 голосов
/ 31 августа 2010

Присвоение ссылки не занимает много времени, но ввод «self» каждый раз отнимает у вас время.

0 голосов
/ 31 августа 2010

В любом случае, нет существенной разницы.Одна из причин создания локальной переменной заключается в том, что если вы собираетесь многократно использовать переменную в __init__, вам не нужно слишком много повторять self..например,

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

против

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

Я не буду сильно беспокоиться о производительности, если у вас нет веских причин для этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...