我最近在Python中的某個ORM文檔對象周圍開發了一個名爲DocumentWrapper
的類,以透明地向它添加一些功能,而無需以任何方式更改其界面。如何使用Python僞造類型
我只是有這個問題。假設我有一些User
對象包裹在其中。調用isinstance(some_var, User)
將返回False
,因爲some_var
確實是DocumentWrapper
的一個實例。
有沒有什麼辦法來僞造Python中的對象類型,使其具有相同的調用返回值True
?
我最近在Python中的某個ORM文檔對象周圍開發了一個名爲DocumentWrapper
的類,以透明地向它添加一些功能,而無需以任何方式更改其界面。如何使用Python僞造類型
我只是有這個問題。假設我有一些User
對象包裹在其中。調用isinstance(some_var, User)
將返回False
,因爲some_var
確實是DocumentWrapper
的一個實例。
有沒有什麼辦法來僞造Python中的對象類型,使其具有相同的調用返回值True
?
測試對象的類型通常是python中的反模式。在某些情況下是有意義的測試「鴨式」的對象,是這樣的:
hasattr(some_var, "username")
但即使這是不可取的,比如有爲什麼表達可能返回false,即使一個包裝的原因與__getattribute__
使用一些魔法來正確代理屬性。
通常傾向於允許變量只採用一種抽象類型,可能還有None
。通過將可選類型的數據傳遞給不同的變量,可以實現基於不同輸入的不同行爲。你想做這樣的事情:
def dosomething(some_user=None, some_otherthing=None):
if some_user is not None:
#do the "User" type action
elif some_otherthing is not None:
#etc...
else:
raise ValueError("not enough arguments")
當然,這一切都假設你有一些控制類型檢查的代碼級別。假設它不是。對於「isinstance()」返回true,該類必須出現在實例的基地中,否則該類必須有__instancecheck__
。既然你不控制這些課程中的任何一個,你就不得不在這個實例中使用一些詭計。做這樣的事情:
def wrap_user(instance):
class wrapped_user(type(instance)):
__metaclass__ = type
def __new__(cls):
pass
def __init__(self):
pass
def __getattribute__(self, attr):
self_dict = object.__getattribute__(type(self), '__dict__')
if attr in self_dict:
return self_dict[attr]
return getattr(instance, attr)
def extra_feature(self, foo):
return instance.username + foo # or whatever
return wrapped_user()
我們正在做的是在時間動態地創建一個新的類,我們需要包裝的實例,從實際繼承被包裝對象的__class__
。如果原始文件有一些我們實際上不想遇到的額外行爲(例如查找具有某個類名稱的數據庫表),我們也會花費額外的麻煩來覆蓋__metaclass__
。這種風格的一個很好的方便之處在於,我們永遠不必在包裝類上創建任何實例屬性,因爲沒有self.wrapped_object
,因爲該值存在於類創建時間。
編輯:正如在評論中指出,上述只適用於一些簡單的類型,如果你需要代理的更詳細的屬性,在目標對象上,(比如說,方法),然後看下面的答案:Python - Faking Type Continued
非常感謝您的寶貴幫助=) – Pierre
爲什麼(看起來多餘)'__metaclass__ = type'必要? –
您已經有效地取消了[描述符協議](https://docs.python.org/3/howto/descriptor.html),例如'wrap_user(obj).extra_feature()'返回一個未綁定的方法。您至少需要檢查從'self_dict'檢索到的對象上的'__get__'方法。查看[Python-Faking Type Continued](https://stackoverflow.com/q/31658171),查看某人提出的後續問題。 –
這聽起來像你想測試你的DocumentWrapper
包裝對象的類型,而不是DocumentWrapper
本身的類型。如果這是正確的,那麼接口DocumentWrapper
需要公開該類型。例如,您可能會向返回包裝對象類型的類添加一個方法。但我不認爲通過使isinstance
的呼叫模糊不清,當它不是時,它會返回True,這是解決此問題的正確方法。
最好的辦法是從用戶繼承DocumentWrapper本身,或混合式模式,從很多類做多inherintance
class DocumentWrapper(User, object)
你也可以假isinstance()通過操縱obj.__class__
結果,但是這是深級別的魔法,不應該這樣做。
謝謝。用戶不是唯一包裝的文件類型,所以這不會不幸的工作。但是,謝謝,我甚至不知道多個繼承可能與Python =) – Pierre
可以使用__instancecheck__
魔術方法覆蓋默認isinstance
行爲:
@classmethod
def __instancecheck__(cls, instance):
return isinstance(instance, User)
這只是如果你想你的對象是一個透明包裝;也就是說,如果您希望DocumentWrapper
的行爲類似User
。否則,只需將包裝的類作爲屬性公開即可。
這是一個Python 3的補充;它帶有抽象基類。你不能這樣做在Python 2
我聽到這個,謝謝......但我使用Python 2.x +( – Pierre
這是在2.6 https://docs.python。 org/2/reference/datamodel.html#customizing-instance-and-subclass-checking – Anentropic
需要注意的是,這個方法在**包裝類**的** meta **類中。 –
下面是使用元類的解決方案,但你需要修改包裝類:
>>> class DocumentWrapper:
def __init__(self, wrapped_obj):
self.wrapped_obj = wrapped_obj
>>> class MetaWrapper(abc.ABCMeta):
def __instancecheck__(self, instance):
try:
return isinstance(instance.wrapped_obj, self)
except:
return isinstance(instance, self)
>>> class User(metaclass=MetaWrapper):
pass
>>> user=DocumentWrapper(User())
>>> isinstance(user,User)
True
>>> class User2:
pass
>>> user2=DocumentWrapper(User2())
>>> isinstance(user2,User2)
False
大燈泡,**你需要修改被包裝的類**所以你只能「僞造」你自己的類型,例如不是字符串或整數 –
在包裝類DocumentWrapper
覆蓋__class__
:
class DocumentWrapper(object):
@property
def __class__(self):
return User
>>> isinstance(DocumentWrapper(), User)
True
這種方式需要對包裝User
類中沒有修改。
Python Mock也是這樣做的(請參閱mock-2.0.0中的mock.py:612,找不到源代碼鏈接到,抱歉)。
多重繼承? – JBernardo
'isinstance(some_var.user,User)'?你究竟在做什麼? –
只是試圖有一個透明的包裝,其行爲*就像包裝的類。包括與isinstance。多重繼承不是解決方案,至少因爲用戶只是DocumentWrapper包裝的許多類中的一個。 (我無法控制這些類,我無法更改它們的繼承樹。) – Pierre