国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

神坑·Python 裝飾類無限遞歸

spacewander / 1449人閱讀

摘要:如今查找結果有誤,說明繼承鏈是錯誤的,因而極有可能是出錯。真相一切都源于裝飾器語法糖。核心思路就是不要更改被裝飾名稱的引用。

本文首發于我的博客,轉載請注明出處

《神坑》系列將會不定期更新一些可遇而不可求的坑
防止他人入坑,也防止自己再次入坑

簡化版問題

現有兩個 View 類:

class View(object):

    def method(self):
        # Do something...
        pass

class ChildView(View):

    def method(self):
        # Do something else ...
        super(ChildView, self).method()

以及一個用于修飾該類的裝飾器函數 register——用于裝飾類的裝飾器很常見(如 django.contrib.adminregister),通常可極大地減少定義相似類時的工作量:

class Mixin(object):
    pass

def register(cls):

    return type(
        "DecoratedView",
        (Mixin, cls),
        {}
    )

這個裝飾器為被裝飾類附加上一個額外的父類 Mixin,以增添自定義的功能。

完整的代碼如下:

class Mixin(object):
    pass

def register(cls):

    return type(
        "DecoratedView",
        (Mixin, cls),
        {}
    )

class View(object):

    def method(self):
        # Do something...
        pass

@register
class ChildView(View):

    def method(self):
        # Do something else ...
        super(ChildView, self).method()

看上去似乎沒什么問題。然而一旦調用 ChildView().method(),卻會報出詭異的 無限遞歸 錯誤:

# ...
File "test.py", line 23, in method
  super(ChildView, self).method()
File "test.py", line 23, in method
  super(ChildView, self).method()
File "test.py", line 23, in method
  super(ChildView, self).method()
RuntimeError: maximum recursion depth exceeded while calling a Python object

【一臉懵逼】

猜想 & 驗證

從 Traceback 中可以發現:是 super(ChildView, self).method() 在不停地調用自己——這著實讓我吃了一驚,因為 按理說 super 應該沿著繼承鏈查找父類,可為什么在這里 super 神秘地失效了呢?

為了驗證 super(...).method 的指向,可以嘗試將該語句改為 print(super(ChildView, self).method),并觀察結果:

>

輸出表明: method 的指向確實有誤,此處本應為 View.method。

super 是 python 內置方法,肯定不會出錯。那,會不會是 super 的參數有誤呢?

super 的簽名為 super(cls, instance),宏觀效果為 遍歷 cls 的繼承鏈查找父類方法,并以 instance 作為 self 進行調用。如今查找結果有誤,說明 繼承鏈是錯誤的,因而極有可能是 cls 出錯。

因此,有必要探測一下 ChildView 的指向。在 method 中加上一句: print(ChildView)

原來,作用域中的 ChildView 已經被改變了。

真相

一切都源于裝飾器語法糖。我們回憶一下裝飾器的等價語法:

@decorator
class Class:
    pass

等價于

class Class:
    pass

Class = decorator(Class)

這說明:裝飾器會更改該作用域內被裝飾名稱的指向

這本來沒什么,但和 super 一起使用時卻會出問題。通常情況下我們會將本類的名稱傳給 super(在這里為 ChildView),而本類名稱和裝飾器語法存在于同一作用域中,從而在裝飾時被一同修改了(在本例中指向了子類 DecoratedView),進而使 super(...).method 指向了 DecoratedView 的最近祖先也就是 ChildView 自身的 method 方法,導致遞歸調用。

解決方案

找到了病因,就不難想到解決方法了。核心思路就是:不要更改被裝飾名稱的引用。

如果你只是想在內部使用裝飾后的新類,可以在裝飾器方法中使用 DecoratedView,而在裝飾器返回時 return cls,以保持引用不變:

def register(cls):

    decorated = type(
        "DecoratedView",
        (Mixin, cls),
        {}
    )

    # Do something with decorated

    return cls

這種方法的缺點是:從外部無法使用 ChildView.another_method 調用 Mixin 上的方法??扇绻娴挠羞@樣的需求,可以采用另一個解決方案:

def register(cls):

    cls.another_method = Mixin.another_method
    return cls

即通過賦值的方式為 cls 添加 Mixin 上的新方法,缺點是較為繁瑣。

兩種方法各有利弊,要根據實際場景權衡使用。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/38085.html

相關文章

  • Python中的動態屬性和特性

    摘要:一利用動態屬性處理數據源屬性在中,數據的屬性和處理數據的方法統稱屬性。處理無效屬性名在中,由于關鍵字被保留,名稱為關鍵字的屬性是無效的。內置函數列出對象的大多數屬性。點號和內置函數會觸發這個方法。 導語:本文章記錄了本人在學習Python基礎之元編程篇的重點知識及個人心得,打算入門Python的朋友們可以來一起學習并交流。 本文重點: 1、了解如何利用動態屬性處理數據;2、掌握Pyth...

    scola666 評論0 收藏0
  • [譯] 屬性訪問、特性和描述符 2

    摘要:不像其他屬性,描述符在類級別上創建。當所有者類被定義時,每個描述符對象都是被綁定到一個不同的類級別屬性的描述符類實例。這必須返回描述符的值。此外,描述符對有一個方便的響應和請求格式。 注:原書作者 Steven F. Lott,原書名為 Mastering Object-oriented Python __getattribute__()方法 __getattribute__()方法是...

    CloudwiseAPM 評論0 收藏0
  • Python中的函數裝飾器和閉包

    摘要:變量查找規則在中一個變量的查找順序是局部環境,閉包,全局,內建閉包引用了自由變量的函數。閉包的作用閉包的最大特點是可以將父函數的變量與內部函數綁定,并返回綁定變量后的函數,此時即便生成閉包的環境父函數已經釋放,閉包仍然存在。 導語:本文章記錄了本人在學習Python基礎之函數篇的重點知識及個人心得,打算入門Python的朋友們可以來一起學習并交流。 本文重點: 1、掌握裝飾器的本質、功...

    caozhijian 評論0 收藏0
  • Python】一文弄懂python裝飾器(附源碼例子)

    摘要:裝飾器的使用符合了面向對象編程的開放封閉原則。三簡單的裝飾器基于上面的函數執行時間的需求,我們就手寫一個簡單的裝飾器進行實現。函數體就是要實現裝飾器的內容。類裝飾器的實現是調用了類里面的函數。類裝飾器的寫法比我們裝飾器函數的寫法更加簡單。 目錄 前言 一、什么是裝飾器 二、為什么要用裝飾器 ...

    liuchengxu 評論0 收藏0
  • Python開啟尾遞歸優化!

    摘要:尾遞歸優化一般遞歸與尾遞歸一般遞歸執行可以看到一般遞歸每一級遞歸都產生了新的局部變量必須創建新的調用棧隨著遞歸深度的增加創建的棧越來越多造成爆棧尾遞歸尾遞歸基于函數的尾調用每一級調用直接返回遞歸函數更新調用棧沒有新局部變量的產生類似迭代的 Python尾遞歸優化 一般遞歸與尾遞歸 一般遞歸: def normal_recursion(n): if n == 1: ...

    junnplus 評論0 收藏0

發表評論

0條評論

spacewander

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<