You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何强制调用Object.__call__?仅允许对象被调用后调用其方法的示例

实现对象需先调用__call__才能访问其他方法的多种方案

当然可以实现这个需求!本质上就是给对象加一个「激活」状态——只有调用过__call__方法激活它之后,其他方法才能正常使用。下面我给你三种不同的实现思路,附带示例代码,你可以根据自己的场景灵活选择:

方案一:实例状态标记 + 方法装饰器

这种方式直观易懂,适合需要精确控制哪些方法需要被保护的场景。咱们先定义一个装饰器,用来检查实例是否已经被调用过;然后给类加一个状态标记,调用__call__时把标记置为True;最后给需要保护的方法挂上这个装饰器就行。

def enforce_call_first(func):
    def wrapper(self, *args, **kwargs):
        if not getattr(self, '_called', False):
            raise PermissionError("必须先调用对象本身(即执行obj())才能使用该方法!")
        return func(self, *args, **kwargs)
    return wrapper

class ControlledObject:
    def __init__(self):
        self._called = False
    
    def __call__(self):
        self._called = True
        print("对象已激活,可以调用其他方法了!")
    
    @enforce_call_first
    def method1(self):
        print("执行method1")
    
    @enforce_call_first
    def method2(self, param):
        print(f"执行method2,参数:{param}")

# 测试
obj = ControlledObject()
# obj.method1()  # 这行会抛出PermissionError
obj()  # 激活对象
obj.method1()  # 正常执行
obj.method2("test")  # 正常执行

方案二:元类全局拦截

如果你的类有很多方法,一个个加装饰器太麻烦,可以用元类来统一拦截所有方法的访问。元类可以帮我们重写实例的__getattribute__方法,每次访问方法时自动检查激活状态,不用逐个装饰。

class CallFirstMeta(type):
    def __call__(cls, *args, **kwargs):
        instance = super().__call__(*args, **kwargs)
        # 给实例添加激活标记
        instance._called = False
        
        # 重写实例的__getattribute__方法
        original_getattr = instance.__getattribute__
        def new_getattr(name):
            # 允许直接访问__call__和特殊方法(比如__init__、__str__)
            if name == "__call__" or name.startswith("__") and name.endswith("__"):
                return original_getattr(name)
            # 检查是否已激活
            if not instance._called:
                raise PermissionError("必须先调用对象本身(即执行obj())才能使用该方法!")
            return original_getattr(name)
        
        instance.__getattribute__ = new_getattr
        return instance

class ControlledObject(metaclass=CallFirstMeta):
    def __call__(self):
        self._called = True
        print("对象已激活,可以调用其他方法了!")
    
    def method1(self):
        print("执行method1")
    
    def method2(self, param):
        print(f"执行method2,参数:{param}")

# 测试
obj = ControlledObject()
# obj.method1()  # 抛出PermissionError
obj()  # 激活
obj.method1()  # 正常执行
obj.method2("test")  # 正常执行

方案三:使用描述符统一控制

描述符是Python中属性访问的底层机制,我们可以用它来包装需要保护的方法,每次调用方法前自动检查激活状态。这种方式比装饰器更灵活,适合需要复用保护逻辑的场景。

class CallFirstDescriptor:
    def __init__(self, func):
        self.func = func
    
    def __get__(self, instance, owner):
        if instance is None:
            return self
        # 检查是否已激活
        if not getattr(instance, '_called', False):
            raise PermissionError("必须先调用对象本身(即执行obj())才能使用该方法!")
        # 返回绑定后的方法
        return self.func.__get__(instance, owner)

class ControlledObject:
    def __init__(self):
        self._called = False
    
    def __call__(self):
        self._called = True
        print("对象已激活,可以调用其他方法了!")
    
    @CallFirstDescriptor
    def method1(self):
        print("执行method1")
    
    @CallFirstDescriptor
    def method2(self, param):
        print(f"执行method2,参数:{param}")

# 测试
obj = ControlledObject()
# obj.method1()  # 抛出PermissionError
obj()  # 激活
obj.method1()  # 正常执行
obj.method2("test")  # 正常执行

这三种方案各有优缺点:

  • 方案一适合少量方法需要保护的场景,灵活可控;
  • 方案二适合大量方法的情况,一次配置全局生效;
  • 方案三适合需要复用保护逻辑的场景,可扩展性强。

你可以根据自己的实际需求来选择~

内容的提问来源于stack exchange,提问作者user3166881

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 09:22:38