首页 > 解决方案 > 将实例变量重新分配给局部方法变量

问题描述

我多次看到以下情况,其中实例变量(例如obj_fooobj_bar)被重新分配为局部方法变量(例如 within 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()

我不确定这是惯例(易于阅读)还是有更重要的目的?

这是不好的做法吗?

这会影响性能吗?

标签: pythonclassnamespaces

解决方案


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

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

速度可能是这里更重要的因素。访问成员变量涉及需要一些时间来解决的各种机制(请参阅__getattr____getattribute____dict__描述符)。此外,变量的 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

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


推荐阅读