TL;DR:Python;我有父子類。我有一個Parent 類的實體,parent. 我可以創建一個 Child 類實體,其super()isparent嗎?
一些特定的用例(可用的解決方法)如下:我想創建一個 Logger 類的實體(來自 Pythonlogging模塊),_log方法多載。方法喜歡logger.info或logger.error呼叫此方法,level指定為INFOorERROR等??,我想替換此方法,不觸及其他方法,并使其無縫運行。
以下是一些不起作用的事情(很好):
- 我不能只從實體繼承
logging.Logger并多載這個方法和建構式,因為Logger實體往往是通過工廠方法創建的,logging.getLogger(name). 所以我不能像這樣多載包裝器的建構式:
class WrappedLogger(logging.Logger):
def __init__(self, ...):
super().__init__(...)
def _log(self, ...):
并期望它一切正常。
- 我可以創建一個包裝類,它提供了我想在結果實體上呼叫的方法,比如
.infoor.error- 但是我必須手動提出所有情況。當方法被埋在堆疊中的幾個呼叫時,它也不能很好地作業_log- 基本上沒有辦法保證對包裝類的任何使用都會呼叫我想要的_log方法 - 我可以像這樣做一些小雜燴:
class WrappedLogger(logging.Logger):
def __init__(self, parent):
self._parent = parent
def _log(...): # overload
def __getattr__(self, method_name):
return getattr(self._parent, method_name)
現在,每當我有這個類的一個實體并呼叫時wrapped.info(...),它都會檢索 的父方法info,呼叫它,然后呼叫它,而self._log后者又指向我的包裝實體。但這感覺非常難看。
- 同樣,我可以采用常規實體
Logger并手動換出該方法;這可能比上面的“聰明”少一點,也不那么丑陋,但同樣令人印象深刻。
這個問題已被問過幾次,但在略有不同的情況下提出了其他解決方案。我不是在尋找解決方法,而是對是否有一種本地方式來構造具有指定父實體的子類實體感興趣。
相關問題:
- 從父類實體創建子類實體,并從子類實體呼叫父方法- 這里有效地建議了一種解決方法
- Python 從父類構造子類- 這里可以在子類的建構式中創建父類
uj5u.com熱心網友回復:
如果您的目標是提供由 使用的自定義記錄器類getLogger,您可以向記錄管理器“注冊”自定義類。
所以,讓我們定義一個自定義記錄器類
from logging import Logger
class MyLogger(Logger):
def _log(self, level, msg, *args, **kwargs) -> None:
print("my logger wants to log:", msg)
super()._log(level, msg, *args, **kwargs)
然后我們告訴全域日志管理器使用這個類。
from logging import setLoggerClass
setLoggerClass(MyLogger)
感謝@Daniil Fajnberg 指出,這是setLoggerClass存在的。
現在getLogger將實體化您的自定義類。
from logging import getLogger
logger = getLogger(__file__)
logger.error("Dummy Error")
這將正常記錄錯誤并列印"my logger wants to log: ..."。
注意:_log您多載的方法是未記錄的。也許有更好的方法來實作你想要的。
uj5u.com熱心網友回復:
如果我理解正確,@Bennet 想要的是 - 他有一些從 Logger 派生的自定義記錄器類(Logger 充當介面),如 Logger1、Logger2 等(選擇的實作將在運行時有所不同)。在每一個之上,他想添加一些只修改每個實作的 _log 函式的功能。
IMO 不應該有任何直接的方法來做到這一點,因為您正在嘗試修改(而不是擴展)不建議用于 OOP 范例的現有類的行為。hacky 方式很聰明(覺得很酷)。
def __getattr__(self, method_name):
return getattr(self._parent, method_name)
(我不認為你可以在 Java 中做同樣的事情) PS 想對此發表評論,但我似乎很窮:)
uj5u.com熱心網友回復:
從您不斷重新表述更一般的問題的方式來看,您似乎誤解了物件創建的作業原理。你要求一個
構造具有指定父實體的子類實體的方法。
沒有“父實體”這樣的概念。繼承是指類,而不是物件。這意味著您需要定義自己,該術語應該是什么意思。您將如何定義“父實體”?它應該是什么,何時以及如何創建?
只是為了證明沒有創建“父實體”的機制,當創建子類實體時,請考慮:
class Foo:
instances = []
def __new__(cls):
print(f"{cls.__name__}.__new__()")
instance = super().__new__(cls)
Foo.instances.append(instance)
return instance
class Bar(Foo):
pass
bar = Bar()
assert len(Foo.instances) == 1
assert Foo.instances[0] is bar
assert type(bar) is Bar
assert isinstance(bar, Foo)
輸出是Bar.__new__()并且顯然斷言通過了。這表明,當我們創建它的實體時,Bar會進一步委托 MRO 鏈上的構造(因為它沒有實作自己的__new__方法),這會導致呼叫Foo.__new__. 它創建物件(通過呼叫object.__new__)并將其放入其instances串列中。Foo也不會創建 class 的另一個實體Foo。
您似乎也誤解了呼叫super該類的作用,因此我建議您查看檔案。簡而言之,它只是一個訪問相關類的優雅工具(同樣:不是實體)。
所以,再一次,你的問題定義不明確。
如果您的意思是(正如@Barmar 建議的那樣)您想要一種將Fooover 實體的所有屬性復制到 實體的方法Bar,那就是另一回事了。在這種情況下,您仍然需要仔細定義“所有屬性”的確切含義。
通常這將參考實體__dict__。但是你也想要它的__slots__復制嗎?方法呢?你也想復制它們嗎?您是要替換Bar實體上的所有內容還是只更新實體上設定的那些屬性Foo?
我希望你明白,我在說什么。我想最簡單的方法就是__dict__用另一個實體的值更新實體:
...
class Bar(Foo):
def update_from(self, obj):
self.__dict__.update(obj.__dict__)
foo = Foo()
foo.n = 1
foo.text = "Hi"
bar = Bar()
bar.update_from(foo)
print(bar.n, bar.text) # output: `1 Hi`
如果你愿意,你當然可以用 的__init__方法來做到這一點Bar。如果 的初始化Foo是確定性的并且實體將初始引數保留在某個地方,則您可以改為呼叫繼承super().__init__自Bar.__init__并將這些初始引數從實體傳遞給它。像這樣的東西:
class Foo:
def __init__(self, x, y):
self.x = x
self.y = y
self.z = x y
class Bar(Foo):
def __init__(self, foo_obj):
super().__init__(foo_obj.x, foo_obj.y)
foo = Foo(2, 3)
bar = Bar(foo)
print(bar.z) # output: `5`
我希望這能讓你更清楚。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/523028.html
標籤:Python哎呀遗产
