K 的一隅

Python Python 语言核心

什么能加括号:Python 的可调用对象

从 callable 检测说起,梳理函数、方法、类构造器与实现了 __call__ 的实例;加括号背后统一的调用协议。

8 分钟阅读 更新于 2026-07-28
本文目录

在 Python 里,后缀 () 不代表“只能是函数”。任何实现了调用协议的对象都可以写在表达式后面加括号——callable(x) 为真时,解释器会走 x(...) 这套机制。函数是最常见的可调用体,但 bound method、类本身、以及带 __call__ 的实例,日常代码里同样频繁出现。把几类可调用对象列清楚,读 sorted(..., key=...)、装饰器、以及 Cla() 创建实例时就不会各讲各的。

调用协议在 CPython 里统一落到 type(obj).__call__(obj, *args, **kwargs)。内置 函数 有自己的 __call__ 实现;__call__ 触发实例创建;你手写类的 __call__ 则让实例行为像函数。装饰器、回调注册表、框架钩子,本质都在传递“能加括号的对象”。

callable:能不能加括号

内置函数 callable(obj) 检查对象是否可调用(通常是有 __call__ 方法,或内置 callable 类型):

python
def f() -> None:
    pass


print(callable(f))              # True
print(callable(len))            # True
print(callable(42))             # False

class C:
    pass


print(callable(C))              # True —— 类可调用
print(callable(C()))            # False —— 普通实例默认不可调用

callable 是静态预判;真正调用时若不可调用,仍会 TypeError: 'int' object is not callable

函数:最直接的 callable

deflambda 产生 function 对象;调用时新建栈帧、绑定参数、执行体:

python
def add(a: int, b: int) -> int:
    return a + b


print(add(2, 3))           # 5
print(callable(add))       # True
print(type(add).__name__)  # function

内置函数(如 len)也是 callable,C 实现,语义一样:obj(*args, **kwargs)

方法:函数绑在实例或类上

类体里的 def method(self, ...) 产生函数;经实例访问时变成 bound methodself 已绑好:

python
class Greeter:
    def hi(self, name: str) -> str:
        return f"hi, {name}"


g = Greeter()
print(g.hi("Ada"))                  # hi, Ada
print(callable(g.hi))               # True
print(Greeter.hi(g, "Bob"))         # hi, Bob —— 未绑定的函数式调用

经类访问时(Greeter.hi)得到的是函数,需要手动传实例作第一个参数。方法仍是 函数 的一种包装,不是新类型魔法。

python
class Counter:
    def __init__(self) -> None:
        self.n = 0

    def inc(self) -> None:
        self.n += 1


c = Counter()
bound = c.inc
print(bound())       # n 变成 1
print(callable(bound))  # True

bound 是 bound method 对象;它记住了 c 和原始函数,所以 bound() 等价于 Counter.inc(c)

类:调用即构造(上一篇的 Cla())

类对象本身 callablePoint(1, 2) 触发类的 __call__,内部走 __new__ / __init__实例

python
class Box:
    def __init__(self, item: str) -> None:
        self.item = item


b = Box("note")
print(callable(Box))     # True
print(type(b).__name__)  # Box

装饰器写法 @some_class 有时也利用“类可调用”:调用类返回实例,等价于把实例注册进某个流程——本质仍是 some_class(...)

数据类、命名元组等语法糖底层仍走 __new__ / __init__,只是帮你生成了模板代码。看到 @dataclass 时不要以为实例是“魔术变出来的”——只是 __init__ 等被自动写好了,创建顺序与普通 class 一致。

call:把实例变成“像函数一样”

在类里定义 __call__实例 也可加括号:

python
class Multiplier:
    def __init__(self, factor: int) -> None:
        self.factor = factor

    def __call__(self, x: int) -> int:
        return x * self.factor


double = Multiplier(2)
print(callable(double))   # True
print(double(21))         # 42

这种模式常见于:策略对象、闭包的可读替代、 functools.partial 的手写版、以及可复用的“函数对象”封装状态。__call__ 可以是任意签名,和普通函数一样支持 *args, **kwargs

python
class Logger:
    def __init__(self, prefix: str) -> None:
        self.prefix = prefix

    def __call__(self, message: str, *, level: str = "INFO") -> None:
        print(f"[{self.prefix}][{level}] {message}")


log = Logger("app")
log("started")
log("warn", level="WARN")

带状态的 callable 实例,和闭包 函数 能表达类似模式:闭包把自由变量藏在 cell 里,__call__ 把状态挂在 self 上。选哪种主要看可读性与是否需要多个入口方法;只有一个“像函数一样调用”的入口时,__call__ 往往更直观。

四类 callable 对照

对象例子括号里发生什么
函数def f / lambda执行函数体
方法obj.method绑定的 self + 执行
MyClass(...)创建并初始化实例
__call__ 的实例obj(...)执行实例的 __call__

底层统一:解释器查找 type(obj).__call__,再分发到具体实现(函数.call、type.call 等)。

可以粗记一张 mental checklist:看到 () 先问“左边表达式的值是谁”,再查 callable;若是 函数方法,直接执行;若是 ,走创建链;若是 实例,找 __call__。调试 TypeError: 'X' object is not callable 时,十有八九是忘了实例默认没有 __call__,或把类名和实例名搞混(该写 Factory() 却写了 Factory 没括号,反之亦然)。

作为参数传递:key=、回调、注册表

API 接受 callable 时,上述四类都能传,只要签名匹配:

python
def apply_twice(fn: callable[[int], int], n: int) -> int:
    return fn(fn(n))


print(apply_twice(lambda x: x + 1, 3))  # 5

m = Multiplier(3)
print(apply_twice(m, 2))                # 18  —— m 作 callable

sorted(items, key=fn)key 也是 callable;传 lambda、普通函数、或带 __call__ 的对象均可。

functools.partial:预填参数的 callable

标准库常把 partial 当作“绑了一半参数的函数”传给 API:

python
from functools import partial

def power(base: int, exp: int) -> int:
    return base ** exp


square = partial(power, exp=2)
print(callable(square))  # True
print(square(5))         # 25
print([square(x) for x in range(4)])  # [0, 1, 4, 9]

partial 对象同样实现调用协议;类型检查里常标注为 Callable[..., T]

类上的 @classmethod 也能当 callable 传给 API:第一个参数是类,适合“从多种输入构造实例”的入口,例如 Dict.fromkeys 那种风格。静态方法 @staticmethod 则不绑实例也不绑类,本质仍是挂在类命名空间里的 函数

python
class Parser:
    @classmethod
    def from_json(cls, raw: str) -> "Parser":
        return cls(raw.strip())

    def __init__(self, text: str) -> None:
        self.text = text


factory = Parser.from_json
print(callable(factory))  # True
p = factory('{"a":1}')
print(p.text)  # {"a":1}

调用失败 vs callable 为假

两类错误不要混为一谈:

python
def needs_two(a: int, b: int) -> int:
    return a + b


print(callable(needs_two))  # True
# needs_two(1)                # TypeError: 少参数
# 42()                        # TypeError: int 不可调用

callable(x) 为真只说明 __call__ 入口;参数不匹配、或内部逻辑抛异常,都在真正调用时才暴露。

标准库 operator 模块里很多名字(如 itemgetterattrgetter)也是 callable 对象,不是 def 写出来的 函数;读第三方库时看到“可调用但 type 不是 function”不必惊讶——回到 __call__ 或 C 实现的内置 callable 即可解释。

常见误解

  • “只有函数能加括号”:类、实例(有 __call__)、方法都可以。
  • 把类实例当函数用却忘了写 __call__C() 可以,C()() 不行,除非定义了 __call__
  • callable(x) 为真但调用仍失败:例如参数个数不对,或 __call__ 内部再抛错——callable 只表示“有调用入口”。
  • 把“可调用”当成“是 function 类型”type(add) 是 function,但 partial、带 __call__ 的实例类型名各不相同,都可以用 callable 检测。
  • 实例默认可调用吗:只有定义了 __call__ 的实例才可再套一层括号;obj()Obj() 是两层不同 callable。

callable 视角串起来:lambda 篇的 函数、类篇的 实例 与构造、创建篇的 Cla(),都是同一条“查找 __call__ 并执行”的路由表。下一批会进入元类、装饰器——它们都站在“对象可被调用或包装 callable”这层模型之上。

小结:谁在后面响应括号

表达式类型callable括号触发
函数 / lambda执行函数体
bound method绑好的 self + 函数体
class__new__ / __init__ 创建 实例
普通 实例否(默认)
__call__实例实例的 __call__

遇到设计问题“该传 函数 还是带 __call__ 的小对象”,优先看是否需要额外状态与多个方法:只有单一调用入口且状态明显属于对象本身时,__call__ 类往往更清晰;否则普通 def 或 lambda 更简单。

callable 也是内置 函数,可以对任意对象做预判,但别在热路径里滥用——直接调用试一次有时更直观。类型检查器用 typing.Callable[[ArgTypes], ReturnType] 描述“期望可调用”,与运行时 callable 不完全等价:某些 C 扩展对象可能可调用却难以静态标注。工程上二者结合:callable 做防御性入口检查,标注做文档与 mypy/pyright 校验。

装饰器(下一篇)会在 函数class 定义时把名字替换成另一个 callable;现在你已经知道“谁可以加括号”,后面只需追问“替换前后各是谁在响应调用”。把 __call__、bound method、class 构造与裸 函数 放在同一张表里,读 @ 语法糖时就不会觉得跳出了对象模型。

再补一个边界:classmethodstaticmethod 也是 callable,经类或 实例 访问时行为不同——经 实例 访问 @classmethod 仍会收到 class 作为第一个参数。它们不是本篇重点,但在注册表、工厂 API 里常作为 callable 值传递,与 __call__ 实例并列出现在“回调候选”列表里。

一句话收束:函数 是最常见的 callable,但协议属于整个对象模型——会加括号的不只有 def。下一篇元类会再往上追一层:谁让 class 本身也能响应 Cla() 这次调用。

回顾与 sorted(..., key=...) 的衔接:key 接受任意 callable,返回值用于比较而非最终输出;因此 __call__ 实例、partial、lambda 都能胜任。选哪种写法,取决于你是否需要对象上额外的状态与方法,而不是 callable 协议本身有何不同。先把 callable(x)x() 分开想,再读装饰器与元类会轻松很多。这是对象模型里承上启下的一节,值得对照代码多敲几次验证输出结果吧。