這可能是一個愚蠢的問題。但是我在撰寫需要從現有包中稍微修改 API 的工具時遇到了這種需求。最小的例子就是這樣
class Foo:
def __init__(self):
print("foo")
class Qux(Foo):
def __init__(self):
print("qux")
class Baz:
def __init__(self):
self.foo = Foo()
def f(self):
# ... a lot of code
Foo()
# ... a lot of code
def g(self):
# ... a lot of code
Foo()
# ... a lot of code
class Bar(Baz):
Baz.Foo = Qux #(my rough idea but it doesn't work)
b = Bar()
b.f()
b.g()
# want to print qux all above
我想在不多載所有Baz使用Foo建構式的方法的情況下實作這一點Qux。有沒有辦法在不觸及超類代碼的情況下做到這一點?
另外,如果有這種情況的名稱,請賜教。我試圖在 Stackoverflow 上搜索解決方案,但沒有運氣。謝謝!
uj5u.com熱心網友回復:
你需要的是所謂的“猴子補丁”——一種可以在 Javascript、Python 和 Ruby 等動態語言中進行的操作,其中模塊級變數或類/實體的屬性在運行時被其他替換。
它在測驗中很受歡迎,因為它允許用模擬替換函式和類——但如果使用得當,可以在生產代碼中作業。
它的用法可以從簡單到復雜,具體取決于那里的排列方式:
如果您可以將程序中的所有出現和用法更改Bar為使用 Qux 而不是 Foo,并且永不回頭 - 您在最簡單的場景中是幸運的,不需要Baz子類,也不需要回滾猴子補丁。
讓我們假設代碼中的用法與您的示例中Foo所示Bar的一樣:代碼假定Foo在模塊級別(Bar宣告的模塊)系結了一個名稱,并且只是 do Foo(),而不是相反,module1.Foo()- 你只需要在實體化任何Bar物件之前替換該名稱。
例如,如果Bar在 中定義module2,您可以在 Python 專案的一開始撰寫(通常是__init__.py專案庫中的檔案,或者,有時是app.py在容器中運行的東西的檔案),您可以這樣做:
import module2
from module3 import Qux
module2.Foo = Qux
# program follows as usual
(...)
from module2 import Bar
def myfun():
x = Bar() # <- when this is run, `Bar` will actually "see" Qux insteaf of Foo
那是簡單的事情。現在,有一些中間場景,比如Foo有時會出現在源代碼中,Foo有時會module1.Foo出現在源代碼中,或者Foo在課堂之外的其他地方使用Bar——有一些方法可以調整上面的想法來處理這個問題。
使用Baz類方法
但是如果你真的需要一個不同的Baz類,那么我們必須微調這個變化——它不再是一個適當的“猴子補丁”了。原始Bar類將指向Foo,并且Baz是一個新的、孤立的類 - 正如您所要求的那樣。但它并不是簡單的繼承——它需要您將Bar類克隆到一個新的Baz. Bar如果需要通過檢查,它可以選擇繼承自,盡管它會用這些方法的克隆isinstance 覆寫類中的所有方法 - 如果在也呼叫它的方法中使用“super()”呼叫需要另一個級別的黑客攻擊。Foo()
我現在不擔心修復super呼叫 - 如果您需要這些呼叫作業,請在評論中提及。
所以要采取的步驟是:
Bar通過使用新名稱呼叫 type 來復制類,Bar作為基礎,并且- 遍歷每個方法的所有成員
Bar并為每個方法克隆該方法。 - 然而,這個克隆應該重新創建函式物件,用指向
__globals__的副本替換它的屬性。FooQux
- 遍歷每個方法的所有成員
實際上,“super()”呼叫會很簡單——我發現當嘗試撰寫一些特殊的代碼讓它作業時,結果證明這是默認行為:人們希望直接從克隆類中的方法傳遞到原類的超類的方法(Bar中使用的方法super()會直接在超類中落地到Baz,跳過Baz中的原方法,即)
def replace_inner(base_cls, new_name, replacements):
from types import FunctionType
class New(base_cls):
pass
New.__name__ = new_name
new_globals = None
for name, obj in base_cls.__dict__.items():
if not isinstance(obj, FunctionType):
try:
setattr(New, name, obj)
except AttributeError:
pass
continue
if not new_globals:
new_globals = copy(obj.__globals__)
new_globals.update(replacements)
new_func = FunctionType(obj.__code__, new_globals, obj.__name__, obj.__defaults__, obj.__closure__)
setattr(New, name, new_func)
return New
這在互動模式下作業(Foo 和 Qux 在您的示例中定義)
In [52]: class A:
...: def __init__(self):
...: print("A")
...:
In [53]: class B(A):
...: def __init__(self):
...: self.f = Foo()
...: super().__init__()
...:
In [54]: C = replace_inner(B, "C", {"Foo": Qux})
In [55]: c = C()
Qux
A
In [56]: c.f
Out[56]: <__main__.Qux at 0x7f0f399ac310>
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/520774.html
標籤:Python哎呀遗产
