[英]Using a metaclass to substitute a class definition?
Python 3.6
我正在嘗試修改第三方庫的行為。
我不想直接更改源代碼。
考慮以下代碼:
class UselessObject(object):
pass
class PretendClassDef(object):
"""
A class to highlight my problem
"""
def do_something(self):
# Allot of code here
result = UselessObject()
return result
我想用我自己的類代替UselessObject
我想知道在我的模塊中使用元類來攔截UselessObject
的創建UselessObject
是一個有效的想法?
編輯
這個答案張貼阿什維尼·喬杜里對同一個問題,可能是使用他人的。 以及以下答案。
PS我還發現'模塊'級別__metaclass__
在python 3中不起作用。所以我最初的問題'它是一個有效的想法'是假的
FWIW,這里有一些代碼說明了Rawing的想法。
class UselessObject(object):
def __repr__(self):
return "I'm useless"
class PretendClassDef(object):
def do_something(self):
return UselessObject()
# -------
class CoolObject(object):
def __repr__(self):
return "I'm cool"
UselessObject = CoolObject
p = PretendClassDef()
print(p.do_something())
產量
I'm cool
如果CoolObject
需要繼承UselessObject
我們甚至可以使用這種技術。 如果我們將CoolObject
的定義CoolObject
為:
class CoolObject(UselessObject):
def __repr__(self):
s = super().__repr__()
return "I'm cool, but my parent says " + s
我們得到這個輸出:
I'm cool, but my parent says I'm useless
這是有效的,因為在執行CoolObject
類定義時,名稱UselessObject
具有其舊定義。
這不是元類的工作。
相反,Python允許您通過稱為“Monkeypatching”的技術來執行此操作,在運行時,您可以在運行時將一個對象替換為另一個對象。
在這種情況下,你會改變thirdyparty.UselessObject
為your.CoolObject
之前調用thirdyparty.PretendClassDef.do_something
這樣做的方法是一個簡單的任務。 因此,假設您在問題上提供的示例代碼段是trirdyparty模塊,在庫中,您的代碼將如下所示:
import thirdyparty
class CoolObject:
# Your class definition here
thirdyparty.UselesObject = Coolobject
您需要注意的事項:您在目標模塊中使用的方式更改UselessObject
指向的對象。
例如,你的PretendedClassDef和UselessObject是在不同的模塊中定義的,如果使用from .useless import UselessObject
,則必須以一種方式處理from .useless import UselessObject
(在這種情況下上面的示例很好),並import .useless
以后再使用它as useless.UselessObject
- 在第二種情況下,你必須在useless
模塊上修補它。
此外,Python的unittest.mock
有一個很好的patch
可調用,可以正確執行monkeypatching並撤消它,如果由於某種原因你希望修改在有限的范圍內有效,比如在你的函數內,或在with
塊內。 如果您不想在程序的其他部分中更改thirdyparty模塊的行為,則可能就是這種情況。
至於元類,如果你需要以這種方式改變你要替換的類的元類,它們只會有任何用處 - 如果你想在類中插入行為,它們只能有用。繼承自UselessObject
。 在這種情況下,它將用於創建本地CoolObject
,你仍然可以執行上述操作,但是在 Python運行任何派生類的UselessObject
的類主體之前要注意你執行UselessObject
,要特別小心從第三方庫中進行任何導入時(如果在同一文件中定義了這些子類,那將會非常棘手)
這只是建立在PM 2Ring和jsbueno的答案上的更多背景:
如果您正在為其他人創建一個庫以用作第三方庫(而不是您使用第三方庫),並且如果您需要CoolObject繼承UselessObject以避免重復,則以下內容可能有助於避免在某些情況下可能會得到的無限遞歸錯誤:
module1.py
class Parent:
def __init__(self):
print("I'm the parent.")
class Actor:
def __init__(self, parent_class=None):
if parent_class!=None: #This is in case you don't want it to actually literally be useless 100% of the time.
global Parent
Parent=parent_class
Parent()
module2.py
from module1 import *
class Child(Parent):
def __init__(self):
print("I'm the child.")
class LeadActor(Actor): #There's not necessarily a need to subclass Actor, but in the situation I'm thinking, it seems it would be a common thing.
def __init__(self):
Actor.__init__(self, parent_class=Child)
a=Actor(parent_class=Child) #prints "I'm the child." instead of "I'm the parent."
l=LeadActor() #prints "I'm the child." instead of "I'm the parent."
請注意,用戶不知道為不同的Actor子類設置parent_class的不同值。 我的意思是,如果你制作多種Actors,你只需要設置一次parent_class,除非你希望它為所有這些都改變。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.