亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

蟒蛇 | 使用子類對象從父類訪問變量是否比從子類訪問相同變量慢?

蟒蛇 | 使用子類對象從父類訪問變量是否比從子類訪問相同變量慢?

收到一只叮咚 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?,F在調用super.add(),這又是:

def add(self):
    ...

self這里仍然會b。所有屬性都直接添加到同一個對象。

可以說解決super().add()呼叫將是一個輕微的開銷,但它絕對可以忽略不計。如果存在的話,訪問屬性的任何區別也會如此。


查看完整回答
反對 回復 2022-10-25
  • 3 回答
  • 0 關注
  • 132 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號