簡體   English   中英

Python:刪除子類中的類屬性

[英]Python: deleting a class attribute in a subclass

我有一個子類,我希望它包含基類中存在的類屬性。

我試過這個,但它不起作用:

>>> class A(object):
...     x = 5
>>> class B(A):
...     del x
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    class B(A):
  File "<pyshell#1>", line 2, in B
    del x
NameError: name 'x' is not defined

我怎樣才能做到這一點?

您可以使用delattr(class, field_name)將其從類定義中刪除。

你不需要刪除它。 只需覆蓋它。

class B(A):
   x = None

或者干脆不要引用它。

或者考慮不同的設計(實例屬性?)。

沒有一個答案對我有用。

例如delattr(SubClass, "attrname") (或其完全等效的del SubClass.attrname )不會“隱藏”父方法,因為這不是方法解析的工作方式。 它會因AttributeError('attrname',)而失敗,因為子類沒有attrname 而且,當然,用None替換屬性實際上並沒有刪除它。

讓我們考慮這個基類:

class Spam(object):
    # Also try with `expect = True` and with a `@property` decorator
    def expect(self):
        return "This is pretty much expected"

我只知道兩種將其子類化的方法,隱藏expect屬性:

  1. 使用__get__引發AttributeError的描述符類。 在屬性查找時,會出現異常,通常與查找失敗無法區分。

    最簡單的方法是聲明一個引發AttributeError 這基本上是@JBernardo 所建議的。

     class SpanishInquisition(Spam): @property def expect(self): raise AttributeError("Nobody expects the Spanish Inquisition!") assert hasattr(Spam, "expect") == True # assert hasattr(SpanishInquisition, "expect") == False # Fails! assert hasattr(SpanishInquisition(), "expect") == False

    但是,這僅適用於實例,不適用於類( hasattr(SpanishInquisition, "expect") == True斷言將被破壞)。

    如果您希望上述所有斷言都成立,請使用以下命令:

     class AttributeHider(object): def __get__(self, instance, owner): raise AttributeError("This is not the attribute you're looking for") class SpanishInquisition(Spam): expect = AttributeHider() assert hasattr(Spam, "expect") == True assert hasattr(SpanishInquisition, "expect") == False # Works! assert hasattr(SpanishInquisition(), "expect") == False

    我相信這是最優雅的方法,因為代碼清晰、通用且緊湊。 當然,一個真的應該三思而后行,如果除去該屬性是他們真正想要的。

  2. 使用__getattribute__魔術方法覆蓋屬性查找。 您可以在子類(或mixin,如下例中,因為我只想編寫一次)中執行此操作,這將隱藏子類實例上的屬性。 如果您還想對子類隱藏該方法,則需要使用元類。

     class ExpectMethodHider(object): def __getattribute__(self, name): if name == "expect": raise AttributeError("Nobody expects the Spanish Inquisition!") return super().__getattribute__(name) class ExpectMethodHidingMetaclass(ExpectMethodHider, type): pass # I've used Python 3.x here, thus the syntax. # For Python 2.x use __metaclass__ = ExpectMethodHidingMetaclass class SpanishInquisition(ExpectMethodHider, Spam, metaclass=ExpectMethodHidingMetaclass): pass assert hasattr(Spam, "expect") == True assert hasattr(SpanishInquisition, "expect") == False assert hasattr(SpanishInquisition(), "expect") == False

    這看起來比上面的方法更糟糕(更冗長,更不通用),但也可以考慮這種方法。

    請注意,這並不在特殊的工作(“魔術”)方法(如__len__ ),因為這些旁路__getproperty__ 有關更多詳細信息,請查看 Python 文檔的特殊方法查找部分。 如果這是您需要撤消的內容,只需覆蓋它並調用object的實現,跳過父級。

不用說,這僅適用於“新式類”(從object繼承的類),因為那里不支持魔術方法和描述符協議。 希望這些都成為過去。

仔細想想你為什么要這樣做; 你可能沒有。 考慮不讓 B 從 A 繼承。

子類化的思想是專門化一個對象。 特別是,一個類的子類應該是父類的有效實例:

>>> class foo(dict): pass
>>> isinstance(foo(), dict)
... True

如果您實現此行為(例如,使用x = property(lambda: AttributeError) ),您就打破了子類化概念,這很糟糕。

也許您可以將x設置為property並在有人嘗試訪問它時引發 AttributeError 。

>>> class C:
        x = 5

>>> class D(C):
        def foo(self):
             raise AttributeError
        x = property(foo)

>>> d = D()
>>> print(d.x)
File "<pyshell#17>", line 3, in foo
raise AttributeError
AttributeError

我也有同樣的問題,我認為我有一個有效的理由刪除子類中的 class 屬性:我的超類(稱為 A)有一個只讀屬性,它提供了該屬性的值,但在我的子類(稱為 B),該屬性是一個讀/寫實例變量。 我發現 Python 正在調用屬性函數,盡管我認為實例變量應該覆蓋它。 我可以創建一個單獨的 getter 函數來用於訪問底層屬性,但這似乎是接口名稱空間的不必要且不雅的混亂(好像這真的很重要)。

事實證明,答案是使用 A 的原始公共屬性創建一個新的抽象超類(稱為 S),並讓 A 和 B 從 S 派生。由於 Python 具有鴨子類型,因此 B 並不重要不擴展 A,我仍然可以在相同的地方使用它們,因為它們隱式地實現了相同的接口。

嘗試這樣做可能是一個壞主意,但是......

由於默認情況下查找Bx工作方式,這似乎不是通過“適當的”繼承來完成的。 獲取Bx ,首先在B查找x ,如果在B找不到,則在A搜索,但另一方面,在設置或刪除Bx只會搜索B 所以例如

>>> class A:
>>>     x = 5

>>> class B(A):
>>>    pass

>>> B.x
5

>>> del B.x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>  
AttributeError: class B has no attribute 'x'

>>> B.x = 6
>>> B.x
6

>>> del B.x
>>> B.x
5

在這里我們看到,首先我們似乎無法刪除Bx因為它不存在( Ax存在並且當您評估Bx時會得到服務)。 然而,通過將Bx設置為 6, Bx將存在,它可以被Bx檢索並被del Bx刪除,通過它它不再存在,因此之后Ax將再次作為對Bx響應。

另一方面,您可以做的是使用元類使Bx引發AttributeError

class NoX(type):
    @property
    def x(self):
        raise AttributeError("We don't like X")

class A(object):
    x = [42]

class B(A, metaclass=NoX):
    pass

print(A.x)
print(B.x)

當然,純粹主義者可能會大喊這打破了 LSP,但這並不是那么簡單。 這一切都歸結為您是否認為通過這樣做創建了一個子類型。 issubclassisinstance方法說是,但 LSP 說不是(並且許多程序員會假設“是”,因為您從A繼承)。

LSP 意味着如果BA的子類型,那么我們可以在可以使用A任何時候使用B ,但是由於在執行此構造時我們不能這樣做,我們可以得出結論, B實際上不是A的子類型,因此是 LSP沒有違反。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM