如何创建一个Python类,它是另一个类的子类,但是没有通过issubclass和/或isinstance测试?

2024-04-29 11:15:35 发布

您现在位置:Python中文网/ 问答频道 /正文

我知道这可能是个糟糕的设计,但我遇到过这样的情况:我需要动态创建类Base的子类Derived,并使Derived的实例通过issubclass(Derived, Base)isinstance(derived_obj, Base)检查(即返回False)。你知道吗

我尝试过很多方法,但都没有成功:

  • Derivedhttps://stackoverflow.com/a/42958013/4909228)中创建名为__class__的属性。这只能用于使检查返回True。你知道吗
  • 重写__instancecheck____subclasscheck__Base方法。这不起作用,因为CPython只在常规检查返回False时调用这些方法。你知道吗
  • __init__期间分配__class__属性。python3.6+中不再允许这样做。你知道吗
  • 生成Derived子类object,并将其所有属性和方法(包括特殊方法)赋给Base。这不起作用,因为某些方法(例如__init__)不能在不是Base子类的实例上调用。你知道吗

这可以用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

Tags: 方法inobjmapforbasereturn属性
2条回答

Overriding the __instancecheck__ and __subclasscheck__ methods of Base. This doesn't work because CPython only calls these methods when conventional checks return 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性能优化,这些优化在某些特殊情况下会阻止调用自定义实例检查挂钩(有关详细信息,请参见here)。特别是,您可能无法对isinstance(obj, Derived)的返回值进行强arm,因为CPython fast path存在完全匹配。你知道吗

最后一点,我同意评论人士的看法,这听起来不是一个很有前途的设计。在这种情况下,似乎应该考虑使用组合而不是继承。你知道吗

正如其他人所指出的:使用构图。你知道吗

生成一个未递归映射的类:

class NotMapped:
    pass

然后使用合成并从中派生类:

class Derived(NotMapped):
    pass

然后在函数开头添加一个case:

def map_structure(fn, obj):

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

将它与多重继承一起用作一种混合。为容器类型创建便利的构造函数,以便NotMapped([1,2,3])按预期工作。你知道吗

相关问题 更多 >