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

如何创建一个Python类,它是另一个类的子类,但未通过issubclass和/或isinstance测试?,python,Python,我知道这可能是个糟糕的设计,但我遇到过这样的情况:我需要动态创建类基的子类派生,并使派生的实例在发行类(派生,基)或isinstance(派生对象,基)检查中失败(即返回False) 我尝试了很多方法,但都没有成功: 在派生的()中创建名为\uuuuu类的属性。这只能用于使检查返回True 重写Base的\uuuu实例检查\uuuuuuuu和\uuuuuuuuuu子项检查\uuuuuuuuu方法。这不起作用,因为CPython仅在常规检查返回False时调用这些方法 在\uuuu init\u

我知道这可能是个糟糕的设计,但我遇到过这样的情况:我需要动态创建类
的子类
派生
,并使
派生
的实例在
发行类(派生,基)
isinstance(派生对象,基)
检查中失败(即返回
False

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

  • 派生的
    ()中创建名为
    \uuuuu类
    的属性。这只能用于使检查返回
    True
  • 重写
    Base
    \uuuu实例检查\uuuuuuuu
    \uuuuuuuuuu子项检查\uuuuuuuuu
    方法。这不起作用,因为CPython仅在常规检查返回
    False
    时调用这些方法
  • \uuuu init\uuuuu
    期间分配
    \uuuuu class\uuuuuuu
    属性。Python 3.6+中不再允许这种情况
  • 制作
    派生的
    子类
    对象
    ,并将其所有属性和方法(包括特殊方法)分配给
    Base的属性和方法
    。这不起作用,因为某些方法(例如,
    \uuu init\uu
    )不能在不是
    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)
def map_structure(fn, obj):

    if isinstance(obj, NotMapped):
        return obj
    # special-case container types etc.
    ...
    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
\uuuu实例检查\uuuuuuuu
\uuuuuuuuuu子项检查\uuuuuuuuu
方法。这不起作用,因为CPython仅在常规检查返回
False
时调用这些方法

这种说法是一种误解。这些钩子是在元类上定义的,而不是在基类()上定义的

类元(类型): ... 定义实例检查(自身,实例): ... 打印(“instancecheck”、self、instance) ... 返回错误 ... 定义子类检查(自我,子类): ... 打印(“子类检查”,自我,子类) ... 返回错误 ... >>>类基(元类=元): ... 通过 ... >>>派生类(基): ... 通过 ... >>>obj=派生的() >>>isinstance(对象,基础) 实例检查 假的 >>>issubclass(派生的,基本的) 子项检查 假的
请注意CPython性能优化,这些优化会阻止在某些特殊情况下调用自定义实例检查挂钩(有关详细信息,请参阅)。特别是,当存在精确匹配时,您可能不会因为出现异常而对
isinstance(obj,Derived)
的返回值进行强制保护


作为最后一点,我同意评论者的观点,这听起来不是一个很有前途的设计。在这种情况下,你应该考虑使用构造函数来继承。

正如其他人指出的:使用构图。

创建一个未递归映射的类:

class NotMapped:
    pass
然后使用composition并从中派生类:

class Derived(NotMapped):
    pass
然后在函数开头添加一个案例:

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)
def map_structure(fn, obj):

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

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

是的,那是个糟糕的设计。您可能应该以某种方式使用组合,您要求的是没有接口继承的实现继承(或者至少是Python与接口继承最接近的近似)。这是一个完美的合成用例。如果对象不应该像
列表
那样迭代,那么它就不是
列表
。简单明了。将其列为一个列表违反了duck类型、Liskov替换以及其他原则。您是否尝试过向实现
\uu\u instancecheck\uuu
的基础添加元类?感谢您给出的精彩答案!还有一个问题:是否可以将元类动态添加到现有类(包括内置类)?这是不可能的(如果已经创建了类,则修改元类没有多大意义,因为元类的主要职责是自定义类创建)。对于用户定义的类型,您可以动态地monkeypatch实例检查和子类检查方法本身,但您当然不能对内置类型执行此操作。