代码之家  ›  专栏  ›  技术社区  ›  Justin

Python自属性重命名

  •  1
  • Justin  · 技术社区  · 6 年前

    如果在一个实例中 self.foo = 1 ,这些(或其他更复杂的示例)之间的区别是什么:

    # 1
    for i in range(10):
        print(self.foo)
    
    # 2
    foo = self.foo
    for i in range(10):
        print(foo)
    

    我目前正在查看一个代码库,其中所有 self 变量被重新分配给其他对象。只是想知道是否有任何理由这样做,并希望从效率和代码清晰度的角度听取意见。

    1 回复  |  直到 6 年前
        1
  •  1
  •   abarnert    6 年前

    考虑以下可能性:

    • 局部变量 self 在循环中间反弹。(对于给定的特定代码,这是不可能的,但可以想象,另一个循环可以做到这一点。)在这种情况下,#1将看到新的 自己 foo 属性,而#2不会。当然,您也可以轻松地重新绑定局部变量 foo公司 作为局部变量 自己 –Ω
    • 自己 是可变的,并且 self.foo 是在循环中间反弹到不同的值。(这可能更容易发生,例如,另一个线程在同一对象上操作。)同样,#1将看到 foo公司 属性,但#2不会。
    • 自己foo公司 本身是可变的,其值在循环的中间发生变化(例如,它是一个列表和一些其他线程调用 append(2) 在上面)。现在#1和#2都将看到新值。
    • 任何东西都是不可变的,或者只是没有代码(包括其他线程上的代码)来改变任何东西。现在#1和#2都将看到原始值,因为没有其他值可以看到。

    如果这些语义差异中有任何一个是相关的,那么您当然想使用任何一个给您正确答案的。


    同时,每次您访问 自己foo公司 ,这需要执行属性查找。在最常见的情况下,这意味着查找 'foo' 在里面 self.__dict__ ,这很快,但不是免费的。您可以轻松创建病理案例,在调用 __getattr__ 它动态创建值并返回一个描述符 __get__ 方法执行一些非平凡的转换。

    访问 foo公司 另一方面,将被编译为使用编译后的索引从帧上的数组中加载一个值。因此,它几乎总是更快,在某些情况下,它可以快得多。

    在大多数现实生活中,这一点都不重要。但偶尔也会这样。在这种情况下,将值复制到循环外部的局部是一个值得进行的微观优化。这在绑定方法中比在普通值中更常见(因为它们总是有一个描述符调用);看见 the unique_everseen recipe in the itertools docs 例如。

    当然,您可以设计一个这样的情况,即这种优化实际上使事情变得缓慢。g、 ,使这个环非常小,但把整个东西放在一个外环中。现在额外的 自己foo公司 每次通过外部循环进行复制(而且循环中涉及的字节码较长,可能会溢出到另一个缓存线),成本可能比节省的成本高得多。


    如果语义上的差异无关紧要,而性能上的差异也无关紧要,那么这只是一个澄清的问题。

    如果表达式比 自己foo公司 ,提取该值并为其命名可能会更清楚。

    但对于这样一个微不足道的情况,只使用 自己foo公司 。通过采取额外的步骤将其复制到局部变量,表明您有理由这样做。所以读者会想,也许 自己foo公司 可以在不同的线程中恢复,或者这个循环可能是代码和 自己foo公司 访问是一个性能问题,等等,浪费时间处理所有这些不相关的内容,而不是像预期的那样读取代码。