Quark's Outlines:Python 模拟可调用对象
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__() 方法的对象视为函数的想法,源自于把行为视为对象一部分的面向对象系统。它让你可以将数据和逻辑合并为一个整体。
| 年份 | 里程碑 |
|---|---|
| 1970s | Smalltalk 中的消息传递启发了“调用函数就是向对象发送消息”的概念。 |
| 1980 | Lisp 和 Scheme 中的函数对象使闭包能够同时携带行为和状态。 |
| 1991 | Python 在 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 来自加利福尼亚州洛杉矶。