为了账号安全,请及时绑定邮箱和手机立即绑定

蟒蛇 | 使用子类对象从父类访问变量是否比从子类访问相同变量慢?

蟒蛇 | 使用子类对象从父类访问变量是否比从子类访问相同变量慢?

收到一只叮咚 2022-10-25 15:46:20
class Animal():    def add(self):        self.weight = 10        self.color = 'Black'class Bird(Animal):    def add(self):        self.feather_type = 'Long'        super().add()b = Bird()b.add()print(b.weight)print(b.color)print(b.feather_type) // will this be faster than above 2 statements ?从父类、子类的对象访问变量是否比直接从子类访问变量慢?如果父类中有很多变量 10+(包括数组和数据模型对象),并且每个变量都在子类中使用,是否建议在每个子类中添加这些变量并从父类中删除以获得更好的性能?(当我写这篇文章时听起来很愚蠢,因为这与整个继承概念相矛盾)将它们作为局部变量存储在子类函数中,然后访问它们会更好吗?(如果多次使用)与代码中一样,变量未在__init__方法中初始化。在这种情况下,这会使程序变慢吗?这样做是因为,并非所有操作都需要类的所有属性。因此,何时并根据需要对它们进行初始化和使用。(注意:注意在执行操作之前创建所需的属性)。如果父类中有很多变量 10+(包括数组和数据模型对象),并且每个变量都在子类中使用,是否建议在每个子类中添加这些变量并从父类中删除以获得更好的性能?(当我写这篇文章时听起来很愚蠢,因为这与整个继承概念相矛盾)
查看完整描述

3 回答

?
心有法竹

TA贡献1866条经验 获得超5个赞

让我们对其进行基准测试并找出答案。为了测试这样的事情,我喜欢使用我编写的模块:timerit

import timerit



class Animal():

    def add(self):

        self.weight = 10

        self.color = 'Black'



class Bird(Animal):

    def add(self):

        self.feather_type = 'Long'

        super().add()



b = Bird()

b.add()


ti = timerit.Timerit(1000000, bestof=100, verbose=2)

for timer in ti.reset('Access weight'):

    with timer:

        b.weight


for timer in ti.reset('Access color'):

    with timer:

        b.color


for timer in ti.reset('Access feather_type'):

    with timer:

        b.feather_type

这导致


Timed Access weight for: 1000000 loops, best of 100

    time per loop: best=347.005 ns, mean=370.222 ± 17.2 ns

Timed Access color for: 1000000 loops, best of 100

    time per loop: best=350.992 ns, mean=367.194 ± 9.5 ns

Timed Access feather_type for: 1000000 loops, best of 100

    time per loop: best=348.984 ns, mean=367.680 ± 11.9 ns

因此,不,它们之间似乎没有任何显着差异。


查看完整回答
反对 回复 2022-10-25
?
杨魅力

TA贡献1811条经验 获得超6个赞

实例属性存储在实例中,因此继承在这里完全无关紧要。请记住,当在子实例上调用父类方法时,该方法作为第一个参数 ( self) 得到的是子实例:


>>> class Foo:

...    def __init__(self):

...        print("In Foo.__init__, self is {}".format(self))

... 

>>> class Bar(Foo): pass

... 

>>> b = Bar()

In Foo.__init__, self is <__main__.Bar object at 0x7fd230244f60>

>>> 

将它们作为局部变量存储在子类函数中,然后访问它们会更好吗?(如果多次使用)

仅适用于紧密循环中使用的属性(属性解析比局部变量解析慢一点)并且分析显示此时存在瓶颈。但也不要指望大幅加速......

实际上,如果您不得不担心这种微优化,那么真正的解决方案是重新考虑您的设计和实现,或者用 C 重新实现代码的关键部分。

与代码中一样,变量未在init方法中初始化。这会使程序变慢吗

不是真的,除了调用的开销obj.add()但是

这样做是因为,并非所有操作都需要类的所有属性。因此,何时并根据需要对它们进行初始化和使用。

错误的设计。如果您想对某些属性进行延迟初始化,请使用一个property或一些自定义描述符。这将确保您的对象无论发生什么都将始终具有预期的属性。

如果父类中有很多变量 10+(包括数组和数据模型对象)

一个类中的“很多”属性通常是一种设计气味。这里没有硬性规定——某些情况确实需要相当多的属性——但是你的类可能有太多的责任,最好将其重构为一组最小的类,每个类都有一个单一的、明确定义的责任。

不,将父类属性初始化复制粘贴到子类无论如何都不会加速您的代码 - 它只会使维护变得更加困难,并增加在更改父类或子类时引入错误的风险。我个人认为这是一个完整的 WTF,但我并不以我与生俱来的外交意识而闻名;-)

编辑

实际上我在这里没有提到的一件事,我在 init 之外创建变量的另一个主要原因是因为我在我的代码中使用了工厂设计模式。我正在使用动态创建类

def _create(pkg): 
    exec('import api.' + pkg + 'Helper as Creator'); 
    return eval('Creator' + '.' + pkg + 'Helper()' )

呃……你可能有兴趣了解 Python 的一些特性,比如importlib.import_modulegetattr。作为一般规则,每当您考虑使用evalor时exec,请确保有更好(=> 更安全、更明确且更易于维护)的解决方案。我已经使用 Python 20 多年了(用于个人和专业项目),我仍然需要找到一个我有正当理由使用evalexec.

此外,您没有发布实际上“动态创建类”但动态创建类不会施加任何限制或解决方法(与“静态”定义相比)的代码部分 - 您仍然可以为您的类提供适当的初始化程序,属性或任何其他自定义描述符等。如果您还使用exec̀̀/eval来构建您的类,那么这里还有更好的方法。

我的2美分...


查看完整回答
反对 回复 2022-10-25
?
白衣非少年

TA贡献1155条经验 获得超0个赞

实例属性都将在实例对象本身上创建。父/子关系在那里无关紧要。

b.add()

这调用:

def add(self):
    ...

self这里将b。现在调用super.add(),这又是:

def add(self):
    ...

self这里仍然会b。所有属性都直接添加到同一个对象。

可以说解决super().add()呼叫将是一个轻微的开销,但它绝对可以忽略不计。如果存在的话,访问属性的任何区别也会如此。


查看完整回答
反对 回复 2022-10-25
  • 3 回答
  • 0 关注
  • 94 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信