如何创建一个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实例检查和子类检查方法本身,但您当然不能对内置类型执行此操作。