python使用元类实现单例模式

文章目录 隐藏

python使用元类实现单例模式

在 Python 中,单例模式(Singleton)是一种设计模式,它确保一个类只有一个实例,并提供全局访问该实例的方法。除了使用装饰器或模块实现单例模式,元类也是一种实现单例模式的高级方法。元类可以控制类的创建过程,从而控制实例化行为。

1. 什么是元类?

元类(metaclass)是用于创建类的“类”。换句话说,类是对象,而元类则是创建这些对象的“模板”。默认情况下,Python 中所有的类都是由 type 元类创建的,但我们可以自定义元类来控制类的实例化过程。

2. 使用元类实现单例模式

为了使用元类实现单例模式,我们需要修改类的创建过程,确保每次创建类的实例时都返回相同的对象。

实现过程:

  • 我们可以通过覆盖元类的 __call__() 方法来控制类的实例化行为。__call__() 方法会在类被调用时触发,即 ClassName() 时调用。
  • __call__() 方法中,检查是否已经有该类的实例。如果没有,创建一个新的实例并保存下来;如果有,直接返回已有的实例。

实现代码

pythonCopy code# 定义单例元类
class SingletonMeta(type):
    _instances = {}  # 用于存储每个类的单例实例

    def __call__(cls, *args, **kwargs):
        # 检查是否已经存在实例
        if cls not in cls._instances:
            # 如果实例不存在,调用父类的 __call__ 方法创建实例
            instance = super().__call__(*args, **kwargs)
            cls._instances[cls] = instance
        return cls._instances[cls]


# 使用元类实现单例的类
class SingletonClass(metaclass=SingletonMeta):
    def __init__(self, value):
        self.value = value

# 测试单例
singleton1 = SingletonClass(10)
singleton2 = SingletonClass(20)

print(singleton1 is singleton2)  # 输出 True
print(singleton1.value)  # 输出 10
print(singleton2.value)  # 输出 10

代码解析

  1. SingletonMeta 元类
    • _instances 是一个类属性,存储类的单例实例。
    • __call__() 方法中,首先检查当前类是否已经在 _instances 中。如果没有,则调用 super().__call__() 来创建类的实例,并将其存储在 _instances 中。否则,直接返回已经存在的实例。
  2. SingletonClass 类
    • 这个类使用 SingletonMeta 作为元类,所以它的实例化过程将受到元类的控制。
    • 实例化 SingletonClass 两次后,singleton1singleton2 实际上是同一个对象。

3. 使用元类实现单例模式的优缺点

优点:

  1. 高可扩展性:元类控制类的创建过程,因此可以很灵活地加入额外的控制逻辑。单例模式只是元类的一个应用,可以扩展元类来做更多的工作。
  2. 全局控制实例化:通过元类可以全局控制类的实例化行为,确保类的任何子类也遵循单例模式。

缺点:

  1. 复杂性增加:元类本身是一个高级特性,相较于简单的装饰器实现单例,元类的代码复杂性较高,可能使代码的可读性降低。
  2. 不常用:对于大多数场景,简单的装饰器或模块就可以满足单例模式的需求,使用元类可能会显得“过度设计”。

4. 对比其他单例实现方式

  • 模块级单例:Python 的模块在第一次导入时会被加载到内存中,之后的导入都是使用相同的模块对象。因此,模块本身可以作为单例模式的一种实现。
  • 装饰器实现单例:可以通过装饰器来包装类的实例化过程,确保只创建一个实例。这种方式比元类简单,也适用于大多数情况。
pythonCopy codedef singleton(cls):
    instances = {}
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance

@singleton
class SingletonClass:
    def __init__(self, value):
        self.value = value

总结

通过使用元类实现单例模式,我们可以对类的实例化过程进行全局控制。尽管元类实现单例模式较为复杂,但它提供了极大的灵活性,尤其是在需要对类的创建过程有更多控制时非常有用。不过,考虑到元类的复杂性,通常只有在必要时才会使用元类,而对于大部分单例模式的需求,装饰器或模块实现更加简单和直观。

考虑线程安全的元类实现单例

ChatGPT said:

ChatGPT

在 Python 中,要实现线程安全的单例模式,并且使用元类控制单例的创建过程,我们可以借助 threading.Lock() 来确保在并发情况下,只有一个线程能够成功创建单例实例。锁机制可以防止多个线程同时访问创建实例的代码,从而保证线程安全。

1. 线程安全的元类实现思路

  • 在单例模式中,通常会遇到多线程同时尝试创建实例的情况,因此需要一个锁来保证在实例创建过程中,只有一个线程能够创建实例,而其他线程等待锁释放后再获取已创建的实例。
  • 在 Python 中,threading.Lock() 提供了一个简单的锁定机制,可以通过 with lock: 语法来隐式加锁,确保线程安全。
  • 核心是覆盖元类的 __call__() 方法来控制实例创建,并在该方法中加锁。

2. 实现线程安全的单例元类

pythonCopy codeimport threading

# 定义一个线程安全的单例元类
class SingletonMeta(type):
    _instances = {}  # 存储类的单例实例
    _lock = threading.Lock()  # 创建一个锁

    def __call__(cls, *args, **kwargs):
        # 确保线程安全的单例创建
        with cls._lock:
            if cls not in cls._instances:
                # 如果实例不存在,则创建一个新的实例
                instance = super().__call__(*args, **kwargs)
                cls._instances[cls] = instance
        return cls._instances[cls]

# 使用元类实现线程安全单例的类
class SingletonClass(metaclass=SingletonMeta):
    def __init__(self, value):
        self.value = value

# 测试单例
singleton1 = SingletonClass(10)
singleton2 = SingletonClass(20)

print(singleton1 is singleton2)  # 输出 True
print(singleton1.value)  # 输出 10
print(singleton2.value)  # 输出 10

3. 代码解析

  1. 线程安全的元类 SingletonMeta
    • SingletonMeta 元类中定义了两个类属性:
      • _instances:用于存储单例实例。
      • _lock:使用 threading.Lock() 创建一个互斥锁,确保多个线程不能同时创建实例。
    • __call__() 方法中使用 with cls._lock: 语法,这是一种隐式加锁的方式,它确保在锁定的代码块中,只有一个线程可以执行。如果已经有其他线程在执行实例化的代码,当前线程会等待锁释放。
  2. SingletonClass
    • 这个类使用 SingletonMeta 作为元类,确保该类的所有实例都是单例。
    • 即使在多线程环境中,SingletonClass 的实例化也将是线程安全的,不会创建多个实例。

4. 线程安全的优缺点

优点:

  1. 线程安全:加锁机制确保多个线程同时调用单例时,不会创建多个实例,保证线程安全。
  2. 全局控制:元类控制类的创建过程,单例实例可以在任何地方全局访问。

缺点:

  1. 性能开销:由于每次实例化都需要加锁,因此在高并发情况下可能会有一定的性能开销。为了优化性能,可以在实例已创建的情况下避免进入加锁代码。

5. 优化加锁(双重检查锁)

为了优化性能,我们可以使用双重检查锁(Double-Checked Locking)技术,减少锁的使用。该技术通过在进入加锁代码前先检查实例是否已创建,避免每次实例化都进行锁操作。

pythonCopy codeimport threading

class SingletonMeta(type):
    _instances = {}
    _lock = threading.Lock()

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:  # 第一次检查,无需加锁
            with cls._lock:
                if cls not in cls._instances:  # 第二次检查,确保线程安全
                    instance = super().__call__(*args, **kwargs)
                    cls._instances[cls] = instance
        return cls._instances[cls]

# 使用元类实现单例类
class SingletonClass(metaclass=SingletonMeta):
    def __init__(self, value):
        self.value = value

# 测试
singleton1 = SingletonClass(10)
singleton2 = SingletonClass(20)

print(singleton1 is singleton2)  # 输出 True
print(singleton1.value)  # 输出 10
print(singleton2.value)  # 输出 10

在这个优化版本中,第一次检查 cls not in cls._instances 时,避免了直接加锁。如果多个线程同时进入到这一行,只有第一个获取到锁的线程会创建实例,其他线程会在加锁之后进行第二次检查,发现实例已经创建,就不会再创建新的实例。

总结

  • 元类实现的线程安全单例:通过元类和 threading.Lock(),我们可以确保即使在多线程环境中,也只会创建一个类的实例。
  • 双重检查锁:进一步优化性能,在高并发情况下减少锁的开销。
  • 使用元类实现单例模式灵活且强大,尤其在需要全局控制类实例的场景中非常有用。不过,如果不需要对类创建过程进行复杂的控制,其他单例模式实现方式(如装饰器或模块)可能更加简单直观。

with cls._lock 的底层实现原理

ChatGPT said:

ChatGPT

with cls._lock 是 Python 的一种上下文管理模式,用来隐式地管理资源的获取与释放。它的底层实现原理是通过上下文管理协议(context management protocol)来自动进行加锁和释放锁的操作。with 语句通过调用对象的 __enter____exit__ 方法来实现这一机制。

底层实现原理

  1. 上下文管理协议
    • 当执行 with cls._lock: 时,Python 会调用 cls._lock.__enter__() 方法,在这个方法中,锁被获取(加锁)。
    • with 代码块执行完毕或发生异常时,Python 会调用 cls._lock.__exit__() 方法,在这个方法中,锁被释放。
  2. 加锁和释放锁的具体步骤
    • 加锁:进入 with 代码块时,Lock 对象的 __enter__() 方法被调用,调用内部的 acquire() 方法。acquire() 方法会尝试获取锁,如果锁已经被其他线程持有,那么当前线程会阻塞,直到锁被释放为止。此时,线程之间会进行协调,确保只有一个线程能够获得该锁。
    • 释放锁:当 with 代码块执行结束后,Lock 对象的 __exit__() 方法被调用,该方法内部会调用 release(),这会释放锁,从而允许其他等待的线程获取锁。

with 语句的基本流程:

pythonCopy codewith cls._lock:
    # 加锁的代码区域
    # 在这里执行线程安全的代码
# 当代码块结束时,自动释放锁

加锁和释放的操作在底层实现上:

  • Python 的 threading.Lock() 是依赖于操作系统提供的低级锁机制实现的。具体来说,底层实现通常依赖于 POSIX threads(pthreads) 机制(Linux、MacOS)或 WinAPI(Windows),这些操作系统原生的同步原语提供了高效的加锁和释放功能。

加锁的关键方法:

  1. acquire(blocking=True, timeout=-1)
    • blocking=True 表示调用线程会阻塞直到获取到锁。
    • timeout 可以用来指定一个等待时间,如果超过时间还没有获得锁,acquire() 会返回 False
    • 当线程调用 acquire() 成功获取锁后,锁会进入持有状态,其他线程在调用 acquire() 时会被阻塞。
  2. release()
    • release() 方法会释放锁,将锁的状态从“已持有”变为“可用”,从而允许其他等待的线程进入临界区。
    • 只有持有锁的线程才可以调用 release(),否则会抛出 RuntimeError 异常。

优缺点:

  • 隐式加锁的优点
    • 代码更加简洁,无需显式地调用 acquire()release()
    • 自动处理异常情况,即使代码块内发生异常,__exit__() 也会确保锁被释放。
  • 显式加锁的优点
    • 提供更灵活的控制,允许在更复杂的条件下决定何时加锁和释放锁。

代码示例:

显式加锁的方式:

pythonCopy codelock = threading.Lock()
lock.acquire()
try:
    # 代码块
finally:
    lock.release()

隐式加锁的方式(使用 with):

pythonCopy codelock = threading.Lock()
with lock:
    # 代码块
# 代码块结束后自动释放锁

两者的功能是一样的,但使用 with 语句更加简洁,并且不需要显式调用 acquire()release(),自动处理异常情况。

小结:

  • with cls._lock 是 Python 中通过上下文管理器自动处理加锁和释放锁的机制。
  • 它通过调用锁对象的 __enter____exit__ 方法来管理锁的生命周期。
  • 底层实现依赖于操作系统的线程库,确保加锁操作的原子性和高效性。

lock.acquire()和lock.release() 底层实现原理

ChatGPT said:

ChatGPT

在 Python 中,lock.acquire()lock.release() 是用于控制线程同步的两个重要操作,常见于 threading 模块中的锁对象。它们的底层实现涉及操作系统提供的原生同步原语,如互斥锁(mutex),确保多个线程不会同时访问共享资源。接下来,我们详细探讨其底层实现原理。

1. lock.acquire() 的底层实现

lock.acquire() 方法用于获取锁,它确保当前线程独占某个共享资源。其具体流程如下:

  • 用户态到内核态的切换: 当一个线程调用 acquire() 获取锁时,首先会查看锁的状态。如果锁当前是“未被持有”(即可用状态),那么该线程将成功获取锁,并进入临界区,锁的状态从“未被持有”切换为“已持有”。如果锁已经被其他线程获取(锁处于“已持有”状态),当前线程就会被阻塞,直到该锁被释放为止。这里会涉及到线程调度的机制:操作系统会将当前线程标记为“等待状态”,并切换到另一个线程执行。
  • 操作系统支持的同步机制: Python 底层的锁是依赖操作系统的同步原语实现的。在 POSIX 系统(如 Linux 和 macOS)中,Python 使用的是 pthread_mutex_t 互斥锁。在 Windows 系统中,Python 使用 WinAPI 提供的互斥锁(CRITICAL_SECTIONMutex)。当调用 acquire() 时,操作系统会执行一个类似于 pthread_mutex_lock()(在 Linux 上)或 WaitForSingleObject()(在 Windows 上)的系统调用。如果锁当前不可用,线程就会进入睡眠状态,等待锁被释放。
  • 自旋锁机制(某些实现中可能使用): 在某些优化的实现中(如 Java 中的锁实现),acquire() 操作可能会使用自旋锁机制,即在短时间内不断轮询锁的状态,而不是立即让线程进入等待队列。自旋锁的使用可以避免频繁的上下文切换,但这通常适用于短期竞争的场景。
  • 阻塞与非阻塞模式acquire() 默认是阻塞的,即如果锁不可用,线程将一直等待,直到锁被释放。如果传递参数 blocking=False,则 acquire() 会尝试获取锁,但如果获取失败(锁已被占用),它会立即返回 False,而不会阻塞线程。

2. lock.release() 的底层实现

lock.release() 方法用于释放当前持有的锁。具体流程如下:

  • 状态切换: 当线程调用 release() 时,它会将锁的状态从“已持有”切换回“未被持有”,并唤醒等待该锁的线程(如果有)。这意味着其他阻塞在该锁上的线程可以重新尝试获取锁。
  • 线程唤醒: 当锁被释放后,操作系统会选择一个等待在该锁上的线程并将其从“等待状态”切换为“可运行状态”,此时该线程将有机会获取锁。唤醒操作是通过内核的调度机制完成的,比如在 POSIX 系统中,pthread_cond_signal()pthread_cond_broadcast() 会被用来通知等待的线程。
  • 解锁后的行为: 成功释放锁后,当前线程可以继续执行,不再受到锁的限制。其他线程则会根据调度机制,按优先级或队列顺序尝试重新获取锁。

3. 操作系统层面的原理

在不同操作系统中,Python 的锁操作有不同的底层实现:

  • Linux (POSIX): 在 Linux 和其他 POSIX 兼容系统上,Python 使用 pthread mutex(互斥锁) 来实现锁的获取和释放。pthread_mutex_lock()pthread_mutex_unlock() 是常用的系统调用。内核会管理互斥锁的状态,并根据锁的当前状态决定线程是否进入睡眠。
  • Windows: 在 Windows 上,Python 使用 Windows API 提供的同步原语。常见的实现包括 CRITICAL_SECTIONMutex。这些同步原语通过 WaitForSingleObject()ReleaseMutex() 来管理线程的同步。

4. 显式加锁和隐式加锁的比较

  • 显式加锁:通过 lock.acquire()lock.release() 显式地控制加锁和解锁。优点是灵活性高,可以精确控制锁的获取和释放时机。缺点是容易引入复杂的锁逻辑,如果忘记释放锁可能会导致死锁。
  • 隐式加锁:通过 with lock: 语句块隐式加锁,利用上下文管理器自动处理锁的获取和释放。优点是简洁,能够自动处理异常情况,避免忘记释放锁。缺点是灵活性不如显式加锁。

5. 常见问题:死锁和性能开销

  • 死锁:如果线程在获取锁后由于某种原因没有释放锁,其他等待该锁的线程将永远阻塞,导致死锁现象。因此,使用 release() 时需要非常小心,通常使用 with 语句可以减少这种风险。
  • 性能开销:频繁的加锁和释放锁操作会带来性能开销,尤其是在多线程竞争严重的场景下。上下文切换、内核调度等开销可能影响性能。因此,在高并发场景中,选择合适的同步机制尤为重要。

总结

  • lock.acquire() 负责获取锁,当锁已被其他线程占用时,当前线程会进入阻塞状态,等待锁被释放。
  • lock.release() 负责释放锁,操作系统会唤醒等待锁的线程,使其能够继续执行。
  • 这些操作底层依赖于操作系统的线程同步机制,如 pthread_mutex(在 Linux 中)或 CRITICAL_SECTION/Mutex(在 Windows 中),通过用户态与内核态的切换管理锁的状态。
  • 使用显式和隐式加锁方式各有优缺点,合理选择方式可以有效减少编程错误和死锁问题。

Python 中with 上下文管理模式

ChatGPT said:

ChatGPT

在 Python 中,with 语句用于简化资源管理的代码,比如打开文件、网络连接、数据库连接等。这种模式被称为上下文管理模式(context management)。它确保在使用资源时正确地获取和释放它们,避免出现资源泄露的情况。下面是对上下文管理模式的详细讲解。

1. 上下文管理器

上下文管理器是一个实现了 __enter__()__exit__() 方法的对象。当使用 with 语句时,上下文管理器会自动调用这些方法。

  • __enter__() 方法:在 with 块开始时执行。它通常用于设置资源并返回需要的对象。
  • __exit__() 方法:在 with 块结束时执行。它负责清理资源(如关闭文件)并处理异常(如果有的话)。

2. 使用 with 语句的优点

  • 简洁性with 语句使得代码更加简洁、易读,特别是在处理文件、网络连接等资源时。
  • 异常处理:即使在 with 块中发生异常,__exit__() 方法也会被调用,确保资源能够被正确释放。
  • 自动管理资源:不需要手动管理资源的获取和释放,减少了出错的可能性。

3. 示例:文件操作

下面是一个使用 with 语句进行文件操作的示例:

pythonCopy codewith open('example.txt', 'r') as file:
    content = file.read()
    print(content)

在这个例子中:

  • open('example.txt', 'r') 返回一个文件对象,该对象是一个上下文管理器。
  • __enter__() 方法在进入 with 块时被调用,返回文件对象 file
  • __exit__() 方法在 with 块结束时被调用,自动关闭文件。

4. 自定义上下文管理器

你可以通过定义类并实现 __enter__()__exit__() 方法来创建自定义上下文管理器。以下是一个示例:

pythonCopy codeclass MyContext:
    def __enter__(self):
        print("Entering the context")
        return self  # 可以返回任何对象

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting the context")
        if exc_type:
            print(f"An exception occurred: {exc_value}")
        return True  # 如果返回 True,抑制异常

with MyContext() as context:
    print("Inside the context")
    # 可以在这里引发异常来测试异常处理
    # raise ValueError("Test error")

5. 上下文管理器的常见用途

  • 文件操作:自动打开和关闭文件。
  • 数据库连接:自动连接和断开数据库。
  • 锁管理:在多线程环境中自动获取和释放锁。
  • 网络连接:管理网络连接的打开和关闭。

6. 小结

上下文管理模式通过使用 with 语句简化了资源管理,使得代码更加简洁和安全。它通过自动调用 __enter__()__exit__() 方法,确保即使发生异常也能正确释放资源。通过自定义上下文管理器,可以方便地管理任何类型的资源,进一步增强了 Python 的可读性和可维护性。

4o mini

You said:

元类实现单例def __call__(cls, *args, **kwargs)

ChatGPT said:

ChatGPT

在 Python 中,元类是用于创建类的类。通过定义一个元类,我们可以控制类的创建过程,包括修改类的属性和方法。使用元类实现单例模式是一种常见的设计模式,用于确保一个类只有一个实例,并提供全局访问点。

1. __call__ 方法

在元类中,__call__ 方法用于控制如何实例化类。它在创建类的实例时被自动调用。在实现单例模式时,我们通常会重写 __call__ 方法,以确保只创建一个实例。

例子:

下面是一个简单的元类实现单例模式的例子:

pythonCopy codeclass SingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            instance = super().__call__(*args, **kwargs)
            cls._instances[cls] = instance
        return cls._instances[cls]

class Singleton(metaclass=SingletonMeta):
    def __init__(self, value):
        self.value = value

# 测试单例模式
s1 = Singleton(1)
s2 = Singleton(2)

print(s1.value)  # 输出: 1
print(s2.value)  # 输出: 1
print(s1 is s2)  # 输出: True

2. 代码分析

  • SingletonMeta:这是我们的元类,它继承自 type。在这个类中,我们定义了一个字典 _instances,用于存储创建的实例。
  • __call__ 方法
    • 当调用 Singleton 时,__call__ 方法会检查 _instances 字典,判断该类是否已经实例化。
    • 如果没有实例化(即 _instances 字典中没有该类的实例),我们调用父类的 __call__ 方法来创建一个新实例,并将其存储在 _instances 中。
    • 如果已经实例化,则直接返回该实例。
  • Singleton:这个类使用 SingletonMeta 作为其元类,因此它具备单例特性。

3. 使用元类实现单例的优缺点

  • 优点
    • 强制单例:元类提供了更强的控制力,能够在类级别上强制执行单例模式。
    • 简洁性:使用元类可以将单例的逻辑封装在元类中,保持代码的简洁和清晰。
  • 缺点
    • 复杂性:元类的使用比常规类复杂,可能会让代码的可读性降低。
    • 调试困难:由于涉及类的创建和调用,调试元类可能会比较麻烦。

4. 小结

通过定义 __call__ 方法,元类可以控制类的实例化过程,从而实现单例模式。这种方法提供了一种灵活且强大的方式来确保某个类只有一个实例。尽管使用元类会增加一些复杂性,但在需要严格控制类实例化的场景中,它是一个非常有用的工具。

2/2

4o mini

You said:

type 的def __call__(self, *args, **kwargs):

ChatGPT said:

ChatGPT

在 Python 中,type 是一个内置的元类,用于动态创建类。在 type 类中,__call__ 方法的实现与我们在自定义元类(例如实现单例模式的 SingletonMeta)中的实现有一些重要的区别。以下是它们之间的一些关键区别:

1. type__call__ 方法

type 的实现中,__call__ 方法主要用于实例化类。它会根据提供的参数创建并返回一个新的类实例。具体来说,type__call__ 方法会执行以下操作:

  • 创建新实例:它会调用 __new__ 方法来创建类的实例。
  • 初始化实例:然后会调用 __init__ 方法来初始化该实例。

以下是 type__call__ 的一个简化示例:

pythonCopy codeclass MyClass:
    def __init__(self, value):
        self.value = value

# type 的 __call__ 方法用于创建实例
MyClassInstance = type(MyClass.__name__, (MyClass,), {})
instance = MyClassInstance(10)

在这个示例中,type 负责创建 MyClass 的实例,并调用 __init__ 方法。

2. 自定义元类的 __call__ 方法

在自定义的元类(如 SingletonMeta)中,__call__ 方法的实现则有不同的目的。主要作用是控制实例化的过程,以确保单例模式的实现。以下是主要的不同点:

  • 实例管理:自定义元类的 __call__ 方法通常会包含逻辑来管理实例,确保同一个类只有一个实例。例如,它会检查是否已经存在实例,如果存在则返回已有实例。
  • 扩展功能:可以在 __call__ 中添加其他的处理逻辑,比如日志记录、参数验证等,提供更多的控制。

以下是自定义元类的 __call__ 的简化示例:

pythonCopy codeclass SingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            instance = super().__call__(*args, **kwargs)
            cls._instances[cls] = instance
        return cls._instances[cls]

3. 关键区别总结

  • 用途type__call__ 主要用于创建类的实例,而自定义元类的 __call__ 主要用于控制和管理实例的创建。
  • 逻辑复杂性:自定义元类可以实现更复杂的逻辑(如单例管理),而 type__call__ 较为简单,主要关注于实例的创建和初始化。

4. 小结

type__call__ 方法和自定义元类的 __call__ 方法虽然名字相同,但其实现目的和逻辑有明显不同。理解这些差异对于深入理解 Python 的类机制和元类的使用非常重要。

0 0 投票数
Article Rating
订阅评论
提醒
guest
0 评论
最旧
最新 最多投票
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x