Почему getattr() намного медленнее, чем self.__dict__.get()?

Пример ниже взят из драйвера базы данных REST на Python 2.7.

В приведенном ниже методе __setattr__, если я использую закомментированную строку getattr(), это снижает производительность создания объекта с 600 rps до 230.

Почему в этом случае getattr() намного медленнее, чем self.__dict__.get()?

class Element(object):

    def __init__(self, client):
        self._client = client
        self._data = {}
        self._initialized = True

    def __setattr__(self, key, value):
        #_initialized = getattr(self, "_initialized", False)
        _initialized = self.__dict__.get("_initialized", False)
        if key in self.__dict__ or _initialized is False:
            # set the attribute normally
            object.__setattr__(self, key, value)
        else:
            # set the attribute as a data property
            self._data[key] = value

person espeed    schedule 20.03.2012    source источник
comment
Кстати, если у вас такая разница в производительности, кэшируйте self.__dict__ в локальной переменной - даже для двух доступов к ней. (dict_ = self.__dict__ в начале __setattr__)   -  person jsbueno    schedule 20.03.2012


Ответы (1)


Вкратце: потому что getattr(foo,bar) делает то же самое, что и foo.bar, что не является то же самое, что просто получить доступ к свойству __dict__ (для начала getattr нужно выбрать правильный __dict__, но происходит гораздо больше).

Пример для иллюстрации:

>>> class A:
...   a = 1
...
>>> class B(A):
...   b = 2
...
>>> dir(B)
['__doc__', '__module__', 'a', 'b']
>>> B.a
1
>>> B.__dict__
{'__module__': '__main__', 'b': 2, '__doc__': None}
>>> B.__dict__['a']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'a'
>>> B.__dict__.get('a')
>>>

Подробности содержатся или связаны здесь: http://docs.python.org/reference/datamodel.html (ищите "getattr").

person Marcin    schedule 20.03.2012
comment
@bereal: тебе нужно перечитать вещи. Необходимые операции для foo.__dict__.get() являются правильным подмножеством getattr(). - person bukzor; 20.03.2012
comment
@bereal Имейте в виду, что байт-коды Python не соответствуют примитивным операциям. У вас может быть очень мало байт-кода для вызова очень сложной функции, в то время как одно простое выражение может сгенерировать намного больше байт-кода. Кроме того, то, что сказал bukzor, является правильным и актуальным. - person Marcin; 20.03.2012
comment
@bereal Это никоим образом не опровергает, что foo.__dict__ делает то же самое, что и getattr(foo, '__dict__'), потому что вызов getattr(foo,'__dict__') может быть более эффективным, чем разыменование произвольного атрибута. - person Marcin; 20.03.2012