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

Sco*_*ott 1 python namespaces class

我已经多次看到过,其中实例变量(例如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()
Run Code Online (Sandbox Code Playgroud)

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

这是不好的做法吗?

这会影响性能吗?

zvo*_*one 5

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

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

速度可能是这里更重要的因素.访问成员变量涉及各种机制(见__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()
Run Code Online (Sandbox Code Playgroud)

时间几乎相同,但有一些不同:

>>> 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
Run Code Online (Sandbox Code Playgroud)

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