[英]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
屬性:
使用從__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
我相信這是最優雅的方法,因為代碼清晰、通用且緊湊。 當然,一個真的應該三思而后行,如果除去該屬性是他們真正想要的。
使用__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,但這並不是那么簡單。 這一切都歸結為您是否認為通過這樣做創建了一個子類型。 issubclass
和isinstance
方法說是,但 LSP 說不是(並且許多程序員會假設“是”,因為您從A
繼承)。
LSP 意味着如果B
是A
的子類型,那么我們可以在可以使用A
任何時候使用B
,但是由於在執行此構造時我們不能這樣做,我們可以得出結論, B
實際上不是A
的子類型,因此是 LSP沒有違反。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.