Quark's Outlines:Python 模拟可调用对象

发布: (2026年3月7日 GMT+8 20:13)
6 分钟阅读
原文: Dev.to

Source: Dev.to

Source:

概览、历史时间线、问题与解决方案

当你在 Python 中使用圆括号时,通常是在调用函数。
但你也可以让自己的对象表现得像函数。如果在类内部定义一个名为 __call__ 的方法,Python 将允许你像调用函数一样“调用”该类的实例。

Python 可调用对象是指任何可以在后面跟随圆括号和参数的对象。这包括:

  • 函数
  • 方法
  • 任何定义了 __call__() 方法的对象

Python 通过添加 __call__ 让你可以让自己的对象可调用。

class Greeter:
    def __call__(self, name):
        return f"Hello, {name}!"

greet = Greeter()
print(greet("Ada"))
# prints:
# Hello, Ada!

即使 greet 不是函数,使用 ("Ada") 调用它时也会执行其 __call__ 方法。

当你希望对象 既能像函数一样工作,又能保存一些状态 时,就会让对象可调用。这在需要对象具备以下特性的情况下非常有用:

  • 记住过去的输入
  • 拥有配置设置
  • 根据设置方式表现不同

可调用对象可以在需要更多控制或上下文时替代函数。

示例:有状态的可调用对象

class Counter:
    def __init__(self):
        self.total = 0

    def __call__(self, amount):
        self.total += amount
        return self.total

add = Counter()
print(add(5))   # 5
print(add(10))  # 15

在这里,对象 add 的行为像函数,但它也记住了内部的 total

Python 的可调用对象行为来源于何处?

Python 将任何具有 __call__() 方法的对象视为函数的想法,源自于把行为视为对象一部分的面向对象系统。它让你可以将数据和逻辑合并为一个整体。

年份里程碑
1970sSmalltalk 中的消息传递启发了“调用函数就是向对象发送消息”的概念。
1980Lisp 和 Scheme 中的函数对象使闭包能够同时携带行为和状态。
1991Python 在 0.9.0 版中引入 __call__(),使带有 __call__ 的对象表现得像函数。
2001内置函数 callable() 被加入,用于检查对象是否可被调用。
2025可调用对象在装饰器和工厂模式中被广泛使用,以实现可扩展性。

如何正确使用 Python 可调用对象?

可调用对象帮助你构建像函数一样工作但功能更强大的工具。下面列出常见模式,每个模式包含问题描述、解决方案和示例。

1. 存储设置的简单可调用对象

问题: 需要一个行为像函数的对象,同时还能保存配置信息。

解决方案: 定义 __call__ 方法,并在实例属性中保存设置。

class Adder:
    def __init__(self, n):
        self.n = n

    def __call__(self, x):
        return x + self.n

add_five = Adder(5)
print(add_five(10))   # 15

2. 能记住历史值的可调用对象

问题: 想让可调用对象跟踪它被调用的次数。

解决方案: 在属性中存储状态,并在 __call__ 中更新它们。

class Tracker:
    def __init__(self):
        self.calls = 0

    def __call__(self):
        self.calls += 1
        return self.calls

count = Tracker()
print(count())  # 1
print(count())  # 2

3. 用类替代复杂函数

问题: 一个简单函数变得越来越复杂,想把它重构为类而不破坏已有代码。

解决方案:__call__ 中实现相同的调用签名,并在原来使用函数的地方改用类实例。

class Square:
    def __call__(self, x):
        return x * x

f = Square()
print(f(6))   # 36

4. 每个实例可配置的行为

问题: 每个实例应运行相同的逻辑,但使用不同的设置。

解决方案:__init__ 中传入配置数据,并在 __call__ 中使用它。

class Greeter:
    def __init__(self, greeting):
        self.greeting = greeting

    def __call__(self, name):
        return f"{self.greeting}, {name}!"

hi    = Greeter("Hi")
hello = Greeter("Hello")

print(hi("Ada"))      # Hi, Ada!
print(hello("Bob"))   # Hello, Bob!

5. 安全地判断对象是否可调用

问题: 你手头有函数、数字和其他对象的混合体,需要只调用那些可调用的对象。

解决方案: 使用 Python 内置的 callable() 函数。

def f(): return 42

class C: pass
obj = C()

print(callable(f))   # True
print(callable(obj)) # False

如果对象定义了 __call__callable() 返回 True;否则返回 False

觉得本文有帮助吗?

点击下方的 点赞 按钮告诉我吧。也欢迎在评论区留下你的想法!如果想看到更多类似内容,别忘了 订阅。感谢阅读!

Mike Vincent – 美国软件工程师兼应用开发者。

关于 Mike Vincent

Mike Vincent 来自加利福尼亚州洛杉矶。

关于迈克·文森特的更多信息

0 浏览
Back to Blog

相关文章

阅读更多 »

RISC‑V 很慢

在进行 Triaging 时,我浏览了 Fedora RISC‑V tracker https://abologna.gitlab.io/fedora-riscv-tracker/ 的条目,已经对大多数进行了分流,目前仍剩下 17 条条目。