Переопределение переменных экземпляра для локальных переменных метода - PullRequest
0 голосов
/ 14 мая 2018

Я много раз видел следующее, где переменные экземпляра (например, obj_foo и obj_bar) переназначаются как локальные переменные метода (например, в call):

class Example:
    def __init__(self, obj_foo, obj_bar):
        self.obj_foo = obj_foo
        self.obj_bar = obj_bar

    def call(self):
        obj_foo, obj_bar = self.obj_foo, self.obj_bar

        obj_foo.do_something()
        obj_bar.do_something_else()

Я не уверен, является ли это условием (легко читаемым) или есть более важная цель?

Это плохая практика?

Это влияет на производительность?

Ответы [ 2 ]

0 голосов
/ 14 мая 2018

Обычно для этого нет причин, но в некоторых случаях это может быть:

  • быстрее (потому что быстрый доступ к локальным переменным)
  • более легко читаемый (потому что он короче)

Скорость, вероятно, является более важным фактором здесь. Доступ к переменным-членам включает различные механизмы (см. __getattr__, __getattribute__, __dict__, дескрипторы ), которые занимают некоторое время Разрешить. Кроме того, метод получения переменной может сделать что-то еще более дорогое.

С другой стороны, локальные переменные в CPython оптимизируются во время компиляции, поэтому фактически нет поиска для переменной с именем 'obj_foo' в __dict__, но вместо этого интерпретатор просто выбирает первую локальную переменную, потому что он знает, что obj_foo является первой локальной переменной без необходимости искать имя.

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


Обычно это не имеет большого значения, но вот пример, демонстрирующий идею:

class A:
    def __init__(self,x):
        self.x=x

    def f(self):
        for i in range(100):
            self.x()

class B:
    def __init__(self,x):
        self.x=x

    def f(self):
        x=self.x
        for i in range(100):
            x()

Время почти такое же, но есть некоторая разница:

>>> timeit.timeit('a.f()', setup='a=A(lambda:None)', globals=locals())
13.119033042000638
>>>
>>> timeit.timeit('b.f()', setup='b=B(lambda:None)', globals=locals())
10.219889547632562

ИМХО, в этом случае разница едва достаточна, чтобы оправдать добавление одной строки кода.

0 голосов
/ 14 мая 2018

Вы можете сделать это только для того, чтобы не записывать self каждый раз.

Однако может быть и более важная причина сделать это: она может полностью изменить семантику.Пример:

def __init__(self, x):
  self.x = 42

def theMethod(self):
  x = self.x
  self.x = 58
  print(x)
  print(self.x)

В этом примере x и self.x не являются взаимозаменяемыми, даже если вы указали x = self.x в первой строке theMethod.Первый print выдаст 42, второй print выведет 58.Это может происходить каждый раз, когда некоторая переменная-член назначается локальной переменной, а затем переопределяется.

Как это влияет на производительность, не совсем очевидно, потому что оба поиска self.x и x должны будут искатьсимвол в словаре: в первом случае - словарь переменных-членов self, во втором - в текущей области видимости.Это может повлиять на производительность как положительно , так и отрицательно, в зависимости от того, сколько и какие другие переменные определены в каждой области.В большинстве необдуманных случаев это может иметь крошечный положительный эффект на производительность.

РЕДАКТИРОВАТЬ: Как указывал @zvone, последний абзац не обязательнодля всех реализаций интерпретатора python.

...