【Python】魔术方法是真的魔法!(第四期)

本系列开篇:【Python】小子!是魔术方法!-CSDN博客

  • 【Python】魔法方法是真的魔法! (第一期)-CSDN博客
  • 【Python】魔法方法是真的魔法! (第二期)-CSDN博客
  • 【Python】魔术方法是真的魔法! (第三期)-CSDN博客

在魔术方法中,__init_subclass__方法是在何时被调用的?__mro_entries__魔术方法在继承关系中的作用是什么?

__init_subclass__方法定义在基类中,当你以这个基类为模板创建一个衍生类时,该方法会被调用。__mro_entries__魔术方法用于解决在继承层次中查找基类(父类)的问题,它返回一个拓扑序列,确保了正确地确定继承链中的基类,从而避免运行时错误。

# --- __init_subclass__ 示例 ---
class PluginBase:
    plugins = []

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs) # 调用父类的 __init_subclass__ (如果存在)
        print(f"PluginBase: 子类 {cls.__name__} 被创建,注册插件...")
        cls.plugins.append(cls) # 注册子类
        # 可以接收来自子类定义的额外参数
        if 'author' in kwargs:
            cls.author = kwargs['author']
        if 'version' in kwargs:
            cls.version = kwargs.get('version', '0.1')

class MyPlugin(PluginBase, author="Alice", version="1.0"):
    def execute(self):
        print(f"{self.__class__.__name__} (作者: {self.author}, 版本: {self.version}) 正在执行...")

class AnotherPlugin(PluginBase, author="Bob"):
    def run(self):
        print(f"{self.__class__.__name__} (作者: {self.author}, 版本: {self.version}) 正在运行...")

# 子类创建时,__init_subclass__ 会被调用
# PluginBase: 子类 MyPlugin 被创建,注册插件...
# PluginBase: 子类 AnotherPlugin 被创建,注册插件...

print(f"\n已注册的插件: {PluginBase.plugins}")
# 已注册的插件: [, ]

plugin_instance1 = MyPlugin()
plugin_instance1.execute()
# MyPlugin (作者: Alice, 版本: 1.0) 正在执行...

plugin_instance2 = AnotherPlugin()
plugin_instance2.run()
# AnotherPlugin (作者: Bob, 版本: 0.1) 正在运行...

print(f"MyPlugin 的作者: {MyPlugin.author}, 版本: {MyPlugin.version}")
# MyPlugin 的作者: Alice, 版本: 1.0
print(f"AnotherPlugin 的作者: {AnotherPlugin.author}, 版本: {AnotherPlugin.version}")
# AnotherPlugin 的作者: Bob, 版本: 0.1


# --- __mro_entries__ 示例 (较为高级,通常用于库和框架中改变MRO行为) ---
# 假设我们有一个类,它希望将其 "mixin" 类以特定方式注入到MRO中

class MixinProvider:
    def __init__(self, *mixins):
        self._mixins = mixins

    def __mro_entries__(self, bases):
        print(f"MixinProvider.__mro_entries__ 被调用,bases: {bases}")
        # 将提供的 mixins 插入到基类列表的前面
        # 注意:这需要非常小心,以确保 MRO 仍然是有效的
        # 这只是一个简化的示例,实际应用会更复杂
        return self._mixins + bases

class BaseClass:
    def greet(self):
        return "Hello from BaseClass!"

class Mixin1:
    def greet(self):
        return "Hello from Mixin1! " + super().greet()
    def feature1(self):
        return "Feature1 from Mixin1"

class Mixin2:
    def greet(self):
        return "Hello from Mixin2! " + super().greet()
    def feature2(self):
        return "Feature2 from Mixin2"

print("\n--- __mro_entries__ 示例 ---")
# 正常继承
class DerivedNormal(Mixin1, BaseClass):
    pass

print(f"DerivedNormal MRO: {DerivedNormal.mro()}")
# DerivedNormal MRO: [, , , ]
dn = DerivedNormal()
print(dn.greet())
# Hello from Mixin1! Hello from BaseClass!

# 使用 __mro_entries__
# MyCombinedClass 的基类将是 (Mixin2, Mixin1) + (BaseClass,) -> (Mixin2, Mixin1, BaseClass)
class MyCombinedClass(MixinProvider(Mixin2, Mixin1), BaseClass):
    pass
# MixinProvider.__mro_entries__ 被调用,bases: (,)

print(f"MyCombinedClass MRO: {MyCombinedClass.mro()}")
# MyCombinedClass MRO: [, , , , ]

cc = MyCombinedClass()
print(cc.greet()) # Mixin2 -> Mixin1 -> BaseClass
# Hello from Mixin2! Hello from Mixin1! Hello from BaseClass!
print(cc.feature1()) # 来自 Mixin1
# Feature1 from Mixin1
print(cc.feature2()) # 来自 Mixin2
# Feature2 from Mixin2

# 注意: __mro_entries__ 是一个非常底层的特性,主要用于高级元编程场景,
# 例如在某些库中动态地构建类的继承层次。不当使用可能导致复杂的 MRO 问题。

__set_name__方法通常在哪里被定义,并且它的主要作用是什么?

__set_name__方法更多地定义在描述符类(descriptor class)中,它作为一个 hook,在构建类实例时被调用,主要用于赋值和初始化类的name属性。

class RevealedAccess:
    """一个数据描述符,在所有者类创建时设置其名称。"""
    def __init__(self, initial_value=None):
        self.value = initial_value
        self.name = None # 将由 __set_name__ 设置

    def __set_name__(self, owner_class, name):
        print(f"RevealedAccess.__set_name__ 被调用: owner={owner_class.__name__}, name='{name}'")
        self.name = name # 描述符实例知道了它在所有者类中的名称

    def __get__(self, instance, owner_class):
        if instance is None:
            # 通过类访问描述符 (e.g., MyClassWithDescriptor.attr_name)
            print(f"RevealedAccess ('{self.name}') 被类 '{owner_class.__name__}' 访问")
            return self # 返回描述符实例本身
        print(f"RevealedAccess ('{self.name}') 从实例 '{instance}' 获取值")
        return self.value

    def __set__(self, instance, value):
        print(f"RevealedAccess ('{self.name}') 在实例 '{instance}' 上设置值为: {value}")
        self.value = value

print("\n--- __set_name__ 示例 ---")
class MyClassWithDescriptor:
    # 当 MyClassWithDescriptor 类被定义时,
    # Python 会自动在 RevealedAccess 实例上调用 __set_name__
    attr1 = RevealedAccess(10)
    # RevealedAccess.__set_name__ 被调用: owner=MyClassWithDescriptor, name='attr1'
    another_attr = RevealedAccess("hello")
    # RevealedAccess.__set_name__ 被调用: owner=MyClassWithDescriptor, name='another_attr'

# 验证描述符实例是否知道了它们的名字
print(f"MyClassWithDescriptor.attr1.name: {MyClassWithDescriptor.attr1.name}")
# RevealedAccess ('attr1') 被类 'MyClassWithDescriptor' 访问
# MyClassWithDescriptor.attr1.name: attr1

obj_desc = MyClassWithDescriptor()

print(f"obj_desc.attr1: {obj_desc.attr1}")
# RevealedAccess ('attr1') 从实例 '<__main__.MyClassWithDescriptor object at ...>' 获取值
# obj_desc.attr1: 10

obj_desc.another_attr = "world"
# RevealedAccess ('another_attr') 在实例 '<__main__.MyClassWithDescriptor object at ...>' 上设置值为: world

print(f"obj_desc.another_attr: {obj_desc.another_attr}")
# RevealedAccess ('another_attr') 从实例 '<__main__.MyClassWithDescriptor object at ...>' 获取值
# obj_desc.another_attr: world

__class_getitem__魔术方法与 typing 有什么关系?

__class_getitem__魔术方法与 typing 有关,它在类用方括号尝试访问值时被调用。例如,在list[int]类型中,通过__class_getitem__方法实现对list中元素类型的检查和处理。

import typing

class MyGenericCollection:
    def __init__(self, item_type):
        self.item_type = item_type
        self.items = []
        print(f"MyGenericCollection 实例已创建,期望类型: {item_type}")

    def add_item(self, item):
        if not isinstance(item, self.item_type):
            raise TypeError(f"项 '{item}' 的类型不是 {self.item_type}")
        self.items.append(item)
        print(f"已添加项: {item}")

    def __repr__(self):
        return f"MyGenericCollection[{self.item_type.__name__}]({self.items})"

    @classmethod
    def __class_getitem__(cls, item_type):
        # 当执行 MyGenericCollection[some_type] 时,此方法被调用
        print(f"MyGenericCollection.__class_getitem__ 被调用,参数: {item_type}")
        if not isinstance(item_type, type) and not isinstance(item_type, typing.TypeVar):
             # 可以添加更复杂的类型检查,例如检查 item_type 是否是 typing 中的特定类型
             pass # 简化示例,实际应用中可能需要更严格的检查

        # 通常,__class_getitem__ 返回一个表示参数化泛型的新类型或一个特殊对象。
        # 在这个示例中,我们返回一个新的类(或一个工厂)或者仅仅是一个占位符。
        # 为了简单起见,这里我们可以返回一个新的MyGenericCollection的"特化"版本,
        # 或者一个简单的包装器对象,或者甚至是我们自己类的一个子类。 Python 的 typing 模块中的泛型类型 (如 List, Dict) 做了更复杂的事情。

        # 简单示例:返回一个类,该类在实例化时使用 item_type
        # return type(f"{cls.__name__}[{item_type.__name__ if hasattr(item_type, '__name__') else item_type}]", (cls,),
        #             {'_original_item_type': item_type})
        # 更常见的模式是返回一个所谓的 "GenericAlias" 对象 (如 list[int] 的类型是 types.GenericAlias)
        # 或者,可以直接返回一个用于创建实例的配置好的类/对象。

        # 为了示例目的,我们这里返回一个简单的对象,它存储了类型参数
        # 但更pythonic的做法是让 MyGenericCollection 本身能够处理这个类型信息
        # 或者返回一个新的类型对象,像 typing.List[T] 那样。

        # 这里我们演示一种模式:让 MyGenericCollection 本身可以被实例化
        # 并且 __class_getitem__ 返回的是类本身,允许稍后使用该类型信息。
        # 但标准的泛型类通常会返回一个新的 types.GenericAlias 对象。

        # 让我们创建一个简单的包装器,以便我们可以实例化它
        class SpecializedCollection(cls): # cls is MyGenericCollection
            # 这个内部类在每次 __class_getitem__ 调用时都会被创建,这可能不是最优的
            # 但它演示了如何将 item_type 绑定到一个新的类上。
            # 更好的方法可能是缓存这些生成的类。
            _bound_item_type = item_type
            def __init__(self, *args, **kwargs):
                super().__init__(self._bound_item_type, *args, **kwargs)

        # 或者,更简单(但不完全符合 typing.Generic 的行为):
        # 直接返回一个 partial-like object or a specialized class instance
        # 这里我们返回一个配置好的类,供以后实例化
        # print(f"  -> 返回一个"特化"的 MyGenericCollection 引用 item_type: {item_type}")
        # return cls # MyGenericCollection 自身需要能处理这个

        # 为了更接近 typing.Generic 的行为,我们应该返回一个代表 MyGenericCollection[item_type] 的对象
        # 这个对象可以不是 MyGenericCollection 本身。Python 的 `typing` 模块有 `GenericAlias`。
        # 我们模拟一个非常简化的版本:
        if isinstance(item_type, tuple):
            # 支持 MyGenericCollection[str, int] 这样的写法 (虽然这里我们只用第一个)
            actual_item_type = item_type[0]
        else:
            actual_item_type = item_type

        # 返回一个新的类,其构造函数绑定了类型参数
        # 这样 MyGenericCollection[int]() 就能直接用了
        return type(f"{cls.__name__}_{actual_item_type.__name__ if hasattr(actual_item_type, '__name__') else str(actual_item_type).replace('.', '_')}", (cls,), {'_defined_item_type': actual_item_type})

print("\n--- __class_getitem__ 示例 ---")

# 当我们写 MyGenericCollection[int] 时,__class_getitem__ 被调用
IntCollectionType = MyGenericCollection[int]
# MyGenericCollection.__class_getitem__ 被调用,参数: 
print(f"IntCollectionType: {IntCollectionType}")
# IntCollectionType: 

StrCollectionType = MyGenericCollection[str]
# MyGenericCollection.__class_getitem__ 被调用,参数: 
print(f"StrCollectionType: {StrCollectionType}")
# StrCollectionType: 

# 现在可以实例化这些"特化"的类型
int_collection = IntCollectionType() # 会调用 MyGenericCollection.__init__(MyGenericCollection_int self, item_type=None) 但我们希望 item_type 是 int
# 为了让它工作,MyGenericCollection 的 __init__ 需要能接收这个类型
# 或者 __class_getitem__ 返回的类需要重写 __init__ 来传递类型参数

# 修改 MyGenericCollection 的 __init__ 和 __class_getitem__ 返回的类
class MyGenericCollection:
    def __init__(self, item_type_from_generic=None, items_data=None):
        # 如果是通过 MyGenericCollection[T]() 创建的,_defined_item_type 会被设置
        self.item_type = getattr(self.__class__, '_defined_item_type', item_type_from_generic)
        if self.item_type is None:
            raise TypeError("MyGenericCollection必须用类型参数实例化,例如 MyGenericCollection[int]()")

        self.items = list(items_data) if items_data is not None else []
        print(f"MyGenericCollection 实例已创建,期望类型: {self.item_type}, 内部类: {self.__class__}")

    def add_item(self, item):
        if not isinstance(item, self.item_type):
            raise TypeError(f"项 '{item}' (类型 {type(item).__name__}) 的类型不是 {self.item_type.__name__}")
        self.items.append(item)
        print(f"已添加项: {item}{self}")

    def __repr__(self):
        type_name = self.item_type.__name__ if hasattr(self.item_type, '__name__') else str(self.item_type)
        return f"{self.__class__.__bases__[0].__name__}[{type_name}]({self.items})"

    @classmethod
    def __class_getitem__(cls, item_type):
        print(f"{cls.__name__}.__class_getitem__ 被调用,参数: {item_type}")

        actual_item_type = item_type[0] if isinstance(item_type, tuple) and len(item_type) > 0 else item_type
        if not isinstance(actual_item_type, type) and not isinstance(actual_item_type, typing.TypeVar):
            # 在 Python 3.9+ 中,很多 typing 中的类型如 List, Dict 自身不是 type 的实例
            # 例如 type(typing.List) 是 typing._SpecialGenericAlias
            # 我们这里简化处理
            print(f"警告: item_type '{actual_item_type}' 可能不是一个标准类型。")

        # 创建并返回一个新的子类,该子类存储了类型参数
        # 这样 MyGenericCollection[int]() 就能创建正确的实例
        type_suffix = actual_item_type.__name__ if hasattr(actual_item_type, '__name__') else str(actual_item_type).replace('.', '_')
        specialized_class_name = f"{cls.__name__}_{type_suffix}"

        # 检查是否已创建过此类,避免重复创建 (可选优化)
        # if specialized_class_name in globals():
        #    return globals()[specialized_class_name]

        new_class = type(specialized_class_name, (cls,), {'_defined_item_type': actual_item_type})
        # globals()[specialized_class_name] = new_class # 可选:注册到全局,使其可被pickle等
        return new_class

print("\n--- __class_getitem__ 示例 (修订后) ---")
IntCollectionType = MyGenericCollection[int]
# MyGenericCollection.__class_getitem__ 被调用,参数: 
print(f"IntCollectionType is: {IntCollectionType}")
# IntCollectionType is: 

int_coll = IntCollectionType() # 实例化
# MyGenericCollection 实例已创建,期望类型: , 内部类: 
int_coll.add_item(10)
# 已添加项: 10 到 MyGenericCollection[int]([10])
try:
    int_coll.add_item("hello")
except TypeError as e:
    print(e)
    # 项 'hello' (类型 str) 的类型不是 int
print(int_coll)
# MyGenericCollection[int]([10])

StrCollectionType = MyGenericCollection[str]
# MyGenericCollection.__class_getitem__ 被调用,参数: 
str_coll = StrCollectionType(["a", "b"])
# MyGenericCollection 实例已创建,期望类型: , 内部类: 
str_coll.add_item("c")
# 已添加项: c 到 MyGenericCollection[str](['a', 'b', 'c'])
print(str_coll)
# MyGenericCollection[str](['a', 'b', 'c'])

# 支持 typing.List[int] 这种作为参数 (虽然我们的实现简化了)
T = typing.TypeVar('T')
ListOfIntType = MyGenericCollection[typing.List[int]]
# MyGenericCollection.__class_getitem__ 被调用,参数: typing.List[int]
# 警告: item_type 'typing.List[int]' 可能不是一个标准类型。
list_int_coll = ListOfIntType()
# MyGenericCollection 实例已创建,期望类型: typing.List[int], 内部类: 
list_int_coll.add_item([1,2])
# 已添加项: [1, 2] 到 MyGenericCollection[List[int]]([[1, 2]])
print(list_int_coll)
# MyGenericCollection[List[int]]([[1, 2]])

# 注意:真正的泛型实现 (如 typing.Generic) 更复杂,
# 会创建 types.GenericAlias 对象,并处理 TypeVar 等细节。
# 这个示例主要演示 __class_getitem__ 的基本机制。

准备方法(__prepare__)在构建类时起到什么作用?

准备方法(__prepare__)用于准备要构建的类的命名空间,通过它可以手动指定一些初始属性和行为,例如返回一个包含特定键值对的字典来初始化类的属性。

import collections

class CustomNamespaceMeta(type):
    @classmethod
    def __prepare__(metacls, name, bases, **kwargs):
        print(f"CustomNamespaceMeta.__prepare__ 被调用为类 '{name}'")
        print(f"  Bases: {bases}")
        print(f"  kwargs: {kwargs}")
        # 返回一个用于类定义的命名空间的对象。
        # 默认是普通的 dict。这里我们使用 OrderedDict 来保持属性定义的顺序。
        # 也可以在这里预置一些值到命名空间中。
        namespace = collections.OrderedDict()
        namespace['__custom_prepared_value__'] = "预置在命名空间中的值"
        print(f"  返回类型为 {type(namespace).__name__} 的命名空间")
        return namespace

    def __new__(metacls, name, bases, namespace, **kwargs):
        print(f"CustomNamespaceMeta.__new__ 被调用为类 '{name}'")
        print(f"  Namespace 内容:")
        for key, value in namespace.items():
            print(f"    {key}: {value}")
        # 我们可以从 kwargs 中获取 __prepare__ 中没有处理的参数
        if 'custom_arg' in kwargs:
            print(f"  从 kwargs 收到: custom_arg={kwargs['custom_arg']}")
            namespace['class_custom_arg'] = kwargs['custom_arg']

        return super().__new__(metacls, name, bases, dict(namespace)) # 将OrderedDict转回dict用于创建类

print("\n--- __prepare__ 示例 ---")
class MyOrderedClass(metaclass=CustomNamespaceMeta, custom_arg="hello_meta"):
    # 类体内的定义会填充到 __prepare__ 返回的命名空间中
    # CustomNamespaceMeta.__prepare__ 被调用为类 'MyOrderedClass'
    #   Bases: ()
    #   kwargs: {'custom_arg': 'hello_meta'}
    #   返回类型为 OrderedDict 的命名空间

    # CustomNamespaceMeta.__new__ 被调用为类 'MyOrderedClass'
    #   Namespace 内容:
    #     __custom_prepared_value__: 预置在命名空间中的值
    #     __module__: __main__
    #     __qualname__: MyOrderedClass
    #     first_method: 
    #     second_attribute: 123
    #     third_method: 
    #   从 kwargs 收到: custom_arg=hello_meta

    def first_method(self):
        pass
    second_attribute = 123
    def third_method(self):
        pass

print(f"\nMyOrderedClass 的属性:")
# 验证 __custom_prepared_value__ 是否存在
print(f"  __custom_prepared_value__: {MyOrderedClass.__custom_prepared_value__}")
# __custom_prepared_value__: 预置在命名空间中的值
print(f"  class_custom_arg: {MyOrderedClass.class_custom_arg}")
# class_custom_arg: hello_meta

# 验证属性顺序 (如果元类在 __new__ 中保留了 OrderedDict)
# 在这个示例中,我们转回了 dict,所以顺序不一定保留在最终的类 __dict__ 中。
# 如果需要保留顺序,元类的 __new__ 需要更复杂的处理,或者 Python 3.7+ dict 默认有序。
# 我们可以在 __new__ 中打印 namespace.items() 来确认顺序:
# (已在 __new__ 的打印中显示)

# 检查 MyOrderedClass.__dict__ (Python 3.7+ dicts are ordered)
print(f"  MyOrderedClass.__dict__ keys: {list(MyOrderedClass.__dict__.keys())}")
# 通常 __module__, __custom_prepared_value__, first_method, second_attribute, third_method, class_custom_arg, __dict__, __weakref__, __doc__ 等
# 顺序可能因Python版本和内部实现而异,但我们能看到自定义的值存在。

__instancecheck____subclasscheck__ 魔术方法的功能是什么?

__instancecheck____subclasscheck__ 魔术方法对应于isinstanceissubclass函数,它们在元类(meta class)中使用,用于检查对象是否属于某个类或是否是某个类的子类。

class DuckLikeMeta(type):
    def __instancecheck__(cls, instance):
        print(f"DuckLikeMeta.__instancecheck__ 被调用: cls={cls.__name__}, instance={type(instance).__name__}")
        # 自定义 isinstance(instance, cls) 的逻辑
        # 如果实例有 quack 和 swim 方法,就认为它是 DuckLike
        return hasattr(instance, 'quack') and callable(instance.quack) and \
               hasattr(instance, 'swim') and callable(instance.swim)

    def __subclasscheck__(cls, subclass):
        print(f"DuckLikeMeta.__subclasscheck__ 被调用: cls={cls.__name__}, subclass={subclass.__name__}")
        # 自定义 issubclass(subclass, cls) 的逻辑
        # 如果子类有 quack 和 swim 方法(通常在其实例上),就认为它是 DuckLike 的子类(概念上的)
        # 注意:这通常检查类本身是否符合某种协议,而不是其实例。
        # 对于协议类,通常检查必要的属性/方法是否在类中定义或继承。
        if not isinstance(subclass, type):
            return False # issubclass 的第二个参数必须是类
        return hasattr(subclass, 'quack') and callable(getattr(subclass, 'quack', None)) and \
               hasattr(subclass, 'swim') and callable(getattr(subclass, 'swim', None))

print("\n--- __instancecheck__ 和 __subclasscheck__ 示例 ---")
class DuckLike(metaclass=DuckLikeMeta):
    # 这个类本身可以为空,元类定义了检查逻辑
    pass

class ActualDuck:
    def quack(self):
        print("嘎嘎!")
    def swim(self):
        print("鸭子在游泳")

class Person:
    def talk(self):
        print("人在说话")
    def walk(self):
        print("人在走路")

class Swan:
    # Swan 有 quack 和 swim 方法,所以应该通过 issubclass(Swan, DuckLike)
    # 并且 swan_instance 应该通过 isinstance(swan_instance, DuckLike)
    def quack(self):
        print("天鹅叫声 (类似嘎嘎)")
    def swim(self):
        print("天鹅在游泳")

class Dog:
    def bark(self):
        print("汪汪!")
    def swim(self):
        print("狗在游泳 (但不会嘎嘎叫)")

duck_instance = ActualDuck()
person_instance = Person()
swan_instance = Swan()
dog_instance = Dog()

print(f"\n检查实例:")
print(f"isinstance(duck_instance, DuckLike): {isinstance(duck_instance, DuckLike)}")
# DuckLikeMeta.__instancecheck__ 被调用: cls=DuckLike, instance=ActualDuck
# isinstance(duck_instance, DuckLike): True

print(f"isinstance(person_instance, DuckLike): {isinstance(person_instance, DuckLike)}")
# DuckLikeMeta.__instancecheck__ 被调用: cls=DuckLike, instance=Person
# isinstance(person_instance, DuckLike): False

print(f"isinstance(swan_instance, DuckLike): {isinstance(swan_instance, DuckLike)}")
# DuckLikeMeta.__instancecheck__ 被调用: cls=DuckLike, instance=Swan
# isinstance(swan_instance, DuckLike): True

print(f"isinstance(dog_instance, DuckLike): {isinstance(dog_instance, DuckLike)}")
# DuckLikeMeta.__instancecheck__ 被调用: cls=DuckLike, instance=Dog
# isinstance(dog_instance, DuckLike): False

print(f"\n检查子类 (概念上的):")
print(f"issubclass(ActualDuck, DuckLike): {issubclass(ActualDuck, DuckLike)}")
# DuckLikeMeta.__subclasscheck__ 被调用: cls=DuckLike, subclass=ActualDuck
# issubclass(ActualDuck, DuckLike): True

print(f"issubclass(Person, DuckLike): {issubclass(Person, DuckLike)}")
# DuckLikeMeta.__subclasscheck__ 被调用: cls=DuckLike, subclass=Person
# issubclass(Person, DuckLike): False

print(f"issubclass(Swan, DuckLike): {issubclass(Swan, DuckLike)}")
# DuckLikeMeta.__subclasscheck__ 被调用: cls=DuckLike, subclass=Swan
# issubclass(Swan, DuckLike): True

print(f"issubclass(Dog, DuckLike): {issubclass(Dog, DuckLike)}")
# DuckLikeMeta.__subclasscheck__ 被调用: cls=DuckLike, subclass=Dog
# issubclass(Dog, DuckLike): False

# 即使没有直接继承,但符合协议
print(f"ActualDuck MRO: {ActualDuck.mro()}") # 不包含 DuckLike
# ActualDuck MRO: [, ]

# 注意: 这些检查是根据元类中定义的逻辑来的,不依赖于传统的继承链。
# 这常用于实现抽象基类 (ABCs) 或协议 (Protocols)。
# Python 的 `abc` 模块和 `typing.Protocol` 提供了更结构化的方式来实现类似功能。

你可能感兴趣的:(Python,python,面试)