将实例变量重新分配给本地方法变量

时间:2018-05-14 16:29:42

标签: python class namespaces

我已经多次看到过,其中实例变量(例如obj_fooobj_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 :(得分:5)

通常,没有理由这样做,但在某些情况下可能是:

  • 更快(因为访问本地变量很快)
  • 更容易阅读(因为它更短)

速度可能是这里更重要的因素。访问成员变量涉及需要一些时间来解决的各种机制(请参阅__getattr____getattribute____dict__descriptors)。此外,变量的getter可能会做更昂贵的事情。

另一方面,局部变量在编译时进行了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

恕我直言,在这种情况下,差异几乎不足以证明添加一行代码是合理的。

答案 1 :(得分:1)

您可以这样做,以避免每次都写出self

但是,也可能有更重要的原因要做到这一点:它可以完全改变语义。例如:

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

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

在此示例中,xself.x不可互换,即使您已在x = self.x的第一行指定了theMethod。第一个print将输出42,第二个print将输出58。每次将某个成员变量分配给局部变量然后重写时,就会发生这种情况。

这对性能的影响并不完全明显,因为查找self.xx都必须在字典中查找符号:在第一种情况下,成员变量的字典{ {2}},在第二种情况下,在当前范围内。它可能会对产生负面影响,具体取决于每个范围中定义的变量数量和其他变量。在大多数非人为的案例中,可能对绩效产生微小的积极影响。

编辑:正如@zvone指出的那样,最后一段并不一定适用于python解释器的所有实现。