[英]How to change a live object's isinstance() behavior?
I want to change the behavior of isinstance
for a live python object.我想更改实时 python 对象的isinstance
行为。
One solution is to create a simple wrapper like the following, but I do not like it:一种解决方案是创建一个如下所示的简单包装器,但我不喜欢它:
class Widget:
def __init__(self, obj):
self.inner_self = obj
lizard = ['head', 'nose', 'tail']
wlizard = Widget(lizard)
assert(isinstance(wlizard, Widget)) # no assertion error thrown
What I don't like about this particular wrapper, is that we must extract the lizard
from wlizard
before we can use the lizard
again我不喜欢这个特殊包装器的一点是,我们必须先从wlizard
提取lizard
,然后才能再次使用lizard
try:
wlizard[0]
except:
print('sorry, wlizard doesn\'t behave like a lizard')
lizard = wlizard.inner_self
print(lizard[0]) # works fine
What I really want is for wlizard
to behave exactly like lizard except that isinstance
returns True
for wlizard
and it returns false for lizard
.我真正想要的是wlizard
的行为与 lizard 完全一样,只是isinstance
为wlizard
返回True
而它为lizard
返回 false 。
The following sort-of works, but has some drawbacks:以下类型的作品,但有一些缺点:
class Widget:
pass
def MakeAWidget(obj):
class Blah(type(obj), Widget):
pass
# inherits type(obj)'s __init__ method
wobj = Blah(obj) # calls type(obj)'s copy constructor
return wobj
One problem is that this only works if type(obj)
's __init__()
method takes in more than just self
;一个问题是,这只适用于type(obj)
的__init__()
方法不仅仅是self
; in particular, that __init__
can take in an instance of type(obj)
, and when it does, it copies the attributes of obj
into self
.特别是, __init__
可以接收type(obj)
的实例,当它接收时,它将obj
的属性复制到self
。 I would like something that works even if obj
does not have a copy constructor.即使obj
没有复制构造函数,我也想要一些可以工作的东西。 Something like the following might be possible to force the existence of a copy-constructor:像下面这样的东西可能会强制复制构造函数的存在:
import copy
class Blah(type(obj), Widget):
def __init__(*args, **kwargs):
if isinstance(args[0], type(obj)):
self = copy.deepcopy(args[0])
return self
return super(type(self), self).__init__(*args, **kwargs)
However, I would rather not copy the object, only modify it in-place.但是,我宁愿不复制对象,而是就地修改它。 Something like the following might be possible, but I am not sure what __BLAH__
would be:可能会出现以下情况,但我不确定__BLAH__
会是什么:
obj = ['apple', 'pear', 'banana']
assert(not isinstance(obj, Widget)) # no error thrown
obj.__BLAH__.append('Widget')
assert(isinstance(obj, Widget)) # no error thrown
Here's something I think does what you want.这是我认为可以满足您的要求的东西。 The wrap()
function dynamically creates a class which is derived from the class of the obj
argument passed to it, and then returns an instance of that class created from it. wrap()
函数动态创建一个类,该类派生obj
它的obj
参数的类,然后返回从它创建的该类的实例。 This assumes the class of obj
supports copy construction (initialization from an instance of the same — or derived — class).这假设obj
类支持复制构造(从同一类或派生类的实例初始化)。
def wrap(obj):
class MetaClass(type):
def __new__(mcls, classname, bases, classdict):
wrapped_classname = '_%s_%s' % ('Wrapped', type(obj).__name__)
return type.__new__(mcls, wrapped_classname, (type(obj),)+bases, classdict)
class Wrapped(metaclass=MetaClass):
pass
return Wrapped(obj)
lizard = ['head', 'nose', 'tail']
wlizard = wrap(lizard)
print(type(wlizard).__name__) # -> _Wrapped_list
print(isinstance(wlizard, list)) # -> True
try:
wlizard[0]
except Exception as exc:
print(exc)
print("sorry, wlizard doesn't behave like lizard")
else:
print('wlizard[0] worked')
I think this is exactly what you want.我认为这正是你想要的。 This solution allows you to decorate any object so that the wrapper instance gets all the methods and attributes of the wrapped one.此解决方案允许您装饰任何对象,以便包装器实例获得包装器实例的所有方法和属性。
This is a metaclass of the wrapper:这是包装器的元类:
class WrapperMeta(type):
@classmethod
def __new_getattr(mcs, method, inst):
if method is None:
method = object.__getattribute__
def new_method(self, key):
try:
return method(self, key)
except AttributeError:
return method(inst.wrappee.fget(self), key)
return new_method
def __new__(mcs, name, bases, kwargs):
if not bases:
bases = (object,)
if len(bases) != 1:
raise TypeError("Wrapper can wrap only one class")
if type(kwargs.get("wrappee")) != property:
raise AttributeError("wrapper class must have a \"wrappee\" property")
inst = type.__new__(mcs, name, bases, kwargs)
inst.__getattribute__ = mcs.__new_getattr(inst.__getattribute__, inst)
return inst
It requires a wrapper to have exactly one parent class (the one you want to wrap), to have "wrappee" property and overrides __getattribute__ in a way you need.它需要一个包装器来拥有一个父类(您想要包装的那个),拥有“wrappee”属性并以您需要的方式覆盖__getattribute__ 。
This is a base class:这是一个基类:
class VeryImportantClass:
def __init__(self):
self.a = 1
def very_important_function(self, n):
return n + self.a
This is a wrapper class:这是一个包装类:
class Wrapper(VeryImportantClass, metaclass=WrapperMeta):
def __init__(self, vii):
self._vii = vii
@property
def wrappee(self):
return self._vii
def very_important_addition(self, n):
return n - self.a * 4
And that is the result:这就是结果:
vii = VeryImportantClass()
vii = Wrapper(vii)
print(vii.very_important_function(5)) # 6
print(vii.very_important_addition(1)) # -3
print(isinstance(vii, VeryImportantClass)) # True
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.