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

Zec*_* Hu 2 python

我知道这可能是糟糕的设计,但我遇到了一种情况,我需要动态创建Derived类的子类,并使实例失败或检查(即 return )。BaseDerivedissubclass(Derived, Base)isinstance(derived_obj, Base)False

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

  • 创建一个名为__class__( Derived/sf/answers/3007060941/ )的属性。这只能用于使支票退回True
  • 重写 的__instancecheck____subclasscheck__方法Base。这不起作用,因为 CPython 仅在常规检查返回时调用这些方法False
  • __class__在 期间分配属性__init__。Python 3.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)
Run Code Online (Sandbox Code Playgroud)

此方法一般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)
Run Code Online (Sandbox Code Playgroud)

如果上述功能能够实现,那么上面可以改为:

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

wim*_*wim 5

重写 的__instancecheck____subclasscheck__方法Base。这不起作用,因为 CPython 仅在常规检查返回时调用这些方法False

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

>>> 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
Run Code Online (Sandbox Code Playgroud)

请注意 CPython 性能优化,它会阻止在某些特殊情况下调用自定义实例检查挂钩(有关详细信息,请参阅此处)。特别是,当存在精确匹配时,您可能不会isinstance(obj, Derived)因为CPython 快速路径而强行返回值。

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