问题:
我已经实现了一个具有相当复杂的内部行为的类,它假装为int
类型,用于所有意图和目的。然后,作为一个樱桃,我真的希望我的类成功通过isinstance()和issubclass()检查int
。到目前为止我失败了。
这是一个我用来测试这个概念的小型演示类。我尝试从object
和int
继承它,并且从int
继承它会使它通过检查,它也会破坏它的一些行为:
#class DemoClass(int):
class DemoClass(object):
_value = 0
def __init__(self, value = 0):
print 'init() called'
self._value = value
def __int__(self):
print 'int() called'
return self._value + 2
def __index__(self):
print 'index() called'
return self._value + 2
def __str__(self):
print 'str() called'
return str(self._value + 2)
def __repr__(self):
print 'repr() called'
return '%s(%d)' % (type(self).__name__, self._value)
# overrides for other magic methods skipped as irrelevant
a = DemoClass(3)
print a # uses __str__() in both cases
print int(a) # uses __int__() in both cases
print '%d' % a # __int__() is only called when inheriting from object
rng = range(10)
print rng[a] # __index__() is only called when inheriting from object
print isinstance(a, int)
print issubclass(DemoClass, int)
本质上,从不可变类继承会产生不可变类,而Python通常会使用基类原始值而不是我精心设计的魔术方法。不好。
我已经看过抽象基类,但它们似乎正在做一些完全相反的事情:它们不是让我的类看起来像内置类型的子类,而是让一个类假装成一个类的超类。 / p>
使用__new__(cls, ...)
似乎也不是解决方案。如果您想要的只是在实际创建它之前修改对象起始值,那就太好了,但我想避开不变性诅咒。尝试使用object.__new__()
也没有结果,因为Python只是抱怨使用object.__new__
来创建int
对象是不安全的。
尝试从(int,dict)继承我的类并使用dict.__new__()
并不是很成功,因为Python apparenty不允许将它们组合在一个类中。
我怀疑可能会在元类中找到解决方案,但到目前为止还没有成功使用它们,主要是因为我的大脑根本没有弯曲到足以理解它们。我还在尝试,但看起来我不会很快得到结果。
所以,问题是:即使我的类非常可变,它是否有可能继承或模仿不可变类型的继承?只要找到解决方案(假设它存在),类继承结构对我来说并不重要。
答案 0 :(得分:4)
这里的问题不是不变性,而是简单的继承。如果DemoClass
是int的子类,则为int
类型的每个对象构造一个真DemoClass
,并且无论何时可以使用int,都将直接使用而不调用__int__
,只需尝试a + 2
。
我宁愿在这里简单地欺骗isinstance
。我只是将DemoClass
的{{1}}子类和隐藏自定义函数后面的内置object
:
isinstance
我可以这样做:
class DemoClass(object):
...
def isinstance(obj, cls):
if __builtins__.isinstance(obj, DemoClass) and issubclass(int, cls):
return True
else:
return __builtins__.isinstance(obj, cls)
答案 1 :(得分:0)
所以,如果我理解正确,你有:
def i_want_int(int_):
# can't read the code; it uses isinstance(int_, int)
您需要致电i_want_int(DemoClass())
,其中DemoClass
可通过int
方式转换为__int__
。
如果你想继承int
,实例'值在创建时确定。
如果您不想在任何地方(int
)编写转化为i_want_int(int(DemoClass()))
,我可以考虑的最简单方法是为i_want_int
定义包装器,进行转换:
def i_want_something_intlike(intlike):
return i_want_int(int(intlike))
答案 2 :(得分:0)
到目前为止,还没有提出替代解决方案,所以这就是我最终使用的解决方案(松散地基于Serge Ballesta的答案):
def forge_inheritances(disguise_heir = {}, disguise_type = {}, disguise_tree = {},
isinstance = None, issubclass = None, type = None):
"""
Monkey patch isinstance(), issubclass() and type() built-in functions to create fake inheritances.
:param disguise_heir: dict of desired subclass:superclass pairs; type(subclass()) will return subclass
:param disguise_type: dict of desired subclass:superclass pairs, type(subclass()) will return superclass
:param disguise_tree: dict of desired subclass:superclass pairs, type(subclass()) will return superclass for subclass and all it's heirs
:param isinstance: optional callable parameter, if provided it will be used instead of __builtins__.isinstance as Python real isinstance() function.
:param issubclass: optional callable parameter, if provided it will be used instead of __builtins__.issubclass as Python real issubclass() function.
:param type: optional callable parameter, if provided it will be used instead of __builtins__.type as Python real type() function.
"""
if not(disguise_heir or disguise_type or disguise_tree): return
import __builtin__
from itertools import chain
python_isinstance = __builtin__.isinstance if isinstance is None else isinstance
python_issubclass = __builtin__.issubclass if issubclass is None else issubclass
python_type = __builtin__.type if type is None else type
def disguised_isinstance(obj, cls, honest = False):
if cls == disguised_type: cls = python_type
if honest:
if python_isinstance.__name__ == 'disguised_isinstance':
return python_isinstance(obj, cls, True)
return python_isinstance(obj, cls)
if python_type(cls) == tuple:
return any(map(lambda subcls: disguised_isinstance(obj, subcls), cls))
for subclass, superclass in chain(disguise_heir.iteritems(),
disguise_type.iteritems(),
disguise_tree.iteritems()):
if python_isinstance(obj, subclass) and python_issubclass(superclass, cls):
return True
return python_isinstance(obj, cls)
__builtin__.isinstance = disguised_isinstance
def disguised_issubclass(qcls, cls, honest = False):
if cls == disguised_type: cls = python_type
if honest:
if python_issubclass.__name__ == 'disguised_issubclass':
return python_issubclass(qcls, cls, True)
return python_issubclass(qcls, cls)
if python_type(cls) == tuple:
return any(map(lambda subcls: disguised_issubclass(qcls, subcls), cls))
for subclass, superclass in chain(disguise_heir.iteritems(),
disguise_type.iteritems(),
disguise_tree.iteritems()):
if python_issubclass(qcls, subclass) and python_issubclass(superclass, cls):
return True
return python_issubclass(qcls, cls)
__builtin__.issubclass = disguised_issubclass
if not(disguise_type or disguise_tree): return # No need to patch type() if these are empty
def disguised_type(obj, honest = False, extra = None):
if (extra is not None):
# this is a call to create a type instance, we must not touch it
return python_type(obj, honest, extra)
if honest:
if python_type.__name__ == 'disguised_type':
return python_type(obj, True)
return python_type(obj)
for subclass, superclass in disguise_type.iteritems():
if obj == subclass:
return superclass
for subclass, superclass in disguise_tree.iteritems():
if python_isinstance(obj, subclass):
return superclass
return python_type(obj)
__builtin__.type = disguised_type
if __name__ == '__main__':
class A(object): pass
class B(object): pass
class C(object): pass
forge_inheritances(disguise_type = { C: B, B: A })
print issubclass(B, A) # prints True
print issubclass(C, B) # prints True
print issubclass(C, A) # prints False - cannot link two fake inheritances without stacking
通过向honest
,isinstance()
和issubclass()
来电提供可选的type()
参数,可以忽略伪造的继承。
用法示例。
让班级B
成为班级A
的假继承人:
class A(object): pass
class B(object): pass
forge_inheritances(disguise_heir = { B: A })
b = B()
print isinstance(b, A) # prints True
print isinstance(b, A, honest = True) # prints False
让课程B
假装 类A
:
class A(object): pass
class B(object): pass
forge_inheritances(disguise_type = { B: A})
b = B()
print type(b) # prints "<class '__main__.A'>"
print type(b, honest = True) # prints "<class '__main__.B'>"
让课程B
和所有的继承人假装成为课程A
:
class A(object): pass
class B(object): pass
class D(B): pass
forge_inheritances(disguise_tree = { B: A})
d = D()
print type(d) # prints "<class '__main__.A'>"
通过堆叠对forge_inheritances()
:
class A(object): pass
class B(object): pass
class C(object): pass
forge_inheritance(disguise_heir = { B: A})
forge_inheritance(disguise_heir = { C: B})
c = C()
print isinstance(c, A) # prints True
显然,这个hack不会以任何方式影响super()
调用和属性/方法继承,这里的主要目的只是在你的情况下欺骗isinstance()
和type(inst) == class
检查无法直接修复它们。