繁体   English   中英

如何创建作为另一个类的子类但未通过issubclass和/或isinstance测试的Python类?

[英]How to create a Python class that is a subclass of another class, but fails issubclass and/or isinstance tests?

我知道这可能是不好的设计,但是我遇到了需要即时创建Base类的Derived子类,并使Derived实例失败的问题,该实例使issubclass(Derived, Base)isinstance(derived_obj, Base)进行检查(即返回False )。

我尝试了多种方法,但没有成功:

  • Derivedhttps://stackoverflow.com/a/42958013/4909228 )中创建名为__class__的属性。 这只能用于使检查返回True
  • 重写Base__instancecheck____subclasscheck__方法。 这是行不通的,因为CPython仅在常规检查返回False时才调用这些方法。
  • __init__期间分配__class__属性。 在Python 3.6及更高版本中不再允许这样做。
  • 制作Derived子类object ,并将其所有属性和方法(包括特殊方法)分配给Base 这是行不通的,因为不能在不是Base的子类的实例上调用某些方法(例如__init__ )。

可以用Python完成吗? 该方法可能是特定于解释器的(代码仅在CPython中运行),并且仅需要针对Python版本3.6+。


为了说明此要求的潜在用法,请考虑以下功能:

def map_structure(fn, obj):
    if isinstance(obj, list):
        return [map_structure(fn, x) for x in obj]
    if isinstance(obj, dict):
        return {k: map_structure(fn, v) for k, v in obj.items()}
    # check whether `obj` is some other collection type
    ...
    # `obj` must be a singleton object, apply `fn` on it
    return fn(obj)

此方法将map推广到可在任意嵌套的结构上使用。 但是,在某些情况下,我们不想遍历某个嵌套结构,例如:

# `struct` is user-provided structure, we create a list for each element
struct_list = map_structure(lambda x: [x], struct)
# somehow add stuff into the lists
...
# now we want to know how many elements are in each list, so we want to
# prevent `map_structure` from traversing the inner-most lists
struct_len = map_structure(len, struct_list)

如果可以实现上述功能,则可以将以上内容更改为:

pseudo_list = create_fake_subclass(list)
struct_list = map_structure(lambda x: pseudo_list([x]), struct)
# ... and the rest of code should work

重写Base__instancecheck____subclasscheck__方法。 这是行不通的,因为CPython仅在常规检查返回False时才调用这些方法。

这个说法是一个误解。 这些挂钩将在元类上定义,而不是在基类( docs )上定义。

>>> class Meta(type): 
...     def __instancecheck__(self, instance): 
...         print("instancecheck", self, instance) 
...         return False 
...     def __subclasscheck__(self, subclass): 
...         print("subclasscheck", self, subclass) 
...         return False 
...
>>> class Base(metaclass=Meta):
...     pass
...
>>> class Derived(Base):
...     pass
...
>>> obj = Derived()
>>> isinstance(obj, Base)
instancecheck <class '__main__.Base'> <__main__.Derived object at 0xcafef00d>
False
>>> issubclass(Derived, Base)
subclasscheck <class '__main__.Base'> <class '__main__.Derived'>
False

请注意,CPython性能优化可防止在某些特殊情况下调用自定义实例检查钩子(有关详细信息,请参见此处 )。 特别是,由于存在完全匹配的CPython快速路径 ,因此您可能无法isinstance(obj, Derived)的返回值。

最后一点,我同意评论者的意见,即这听起来并不是一个很有前途的设计。 在这种情况下,您似乎应该考虑使用合成而不是继承。

正如其他人指出的那样:使用组合。

制作一个没有递归映射的类:

class NotMapped:
    pass

然后使用composition并从中派生您的类:

class Derived(NotMapped):
    pass

然后在函数的开头添加一个大小写:

def map_structure(fn, obj):

    if isinstance(obj, NotMapped):
        return obj
    # special-case container types etc.
    ...
    return fn(obj)

将此与多重继承一起用作混合。 为容器类型创建便利条件,因此NotMapped([1、2、3])会按预期工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM