Python使用_call_让实例对象可调用的方法

 更新时间:2026年09月04日 08:37:23   作者:星河耀银海  
在Python中,函数是可调用对象——你给它加括号传参就能执行,但你知道吗?你可以让自己的类的实例也变成可调用的——就像函数一样,这就是__call__,本文用通俗易懂的方式讲解__call__的原理和用法,教你如何让对象像函数一样被调用,需要的朋友可以参考下

一、开篇:把对象变成函数

在Python中,函数是可调用对象——你给它加括号传参就能执行。但你知道吗?你可以让自己的类的实例也变成可调用的——就像函数一样。

这就是__call__:

class Greeter:
    """打招呼——本身就能被调用"""
    def __init__(self, greeting="你好"):
        self.greeting = greeting

    def __call__(self, name):
        """让Greeter的实例可以像函数一样被调用"""
        return f"{self.greeting},{name}!"

# 实例化为一个"函数"
greet = Greeter("早上好")

# 直接调用实例——就像调用函数!
print(greet("张三"))       # 早上好,张三!
print(greet("李四"))       # 早上好,李四!

# 验证:这是可调用的
print(callable(greet))     # True

# 创建另一个不同配置的"函数"
greet_en = Greeter("Hello")
print(greet_en("Alice"))   # Hello,Alice!

__call__将对象从"数据的容器"升级为"行为的载体"。它最强大的应用是创建带状态的函数——函数可以"记住"配置和数据。

二、__call__的基本原理

2.1 可调用对象检测

# callable()检查对象是否可调用
print(callable(print))       # True —— 函数
print(callable(lambda: 1))   # True —— lambda
print(callable("hello"))     # False —— 字符串不可调用
print(callable(42))          # False —— 数字不可调用

# 检查对象是否定义了__call__
class CallableClass:
    def __call__(self):
        return "我被调用了!"

class RegularClass:
    pass

print(callable(CallableClass()))  # True
print(callable(RegularClass()))   # False

# 类本身也是可调用的——类的__call__创建实例
print(callable(RegularClass))     # True —— 调用类创建实例

2.2 __call__可以接收任意参数

# __call__像普通方法一样——可以定义任何参数

class Multiplier:
    """乘法器——记住factor,接受任意数字"""
    def __init__(self, factor):
        self.factor = factor
        self.call_count = 0  # 带状态!

    def __call__(self, x):
        self.call_count += 1
        return x * self.factor

    def stats(self):
        return f"被调用了{self.call_count}次"

double = Multiplier(2)
triple = Multiplier(3)

print(double(10))    # 20
print(double(5))     # 10
print(triple(10))    # 30
print(double.stats())  # 被调用了2次

# 接收多个参数
class Adder:
    def __call__(self, a, b, *args):
        total = a + b + sum(args)
        return total

add = Adder()
print(add(1, 2))          # 3
print(add(1, 2, 3, 4, 5)) # 15

三、__call__的经典应用

3.1 装饰器——__call__的明星用法

# 基于类的装饰器——比函数装饰器更灵活(带状态)

class CountCalls:
    """统计函数被调用次数的装饰器"""
    def __init__(self, func):
        self.func = func
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"→ {self.func.__name__} 第{self.count}次被调用")
        return self.func(*args, **kwargs)

@CountCalls
def greet(name):
    return f"Hello, {name}!"

@CountCalls
def calculate(a, b):
    return a + b

print(greet("Alice"))     # → greet 第1次被调用  /  Hello, Alice!
print(greet("Bob"))       # → greet 第2次被调用  /  Hello, Bob!
print(calculate(3, 5))    # → calculate 第1次被调用  /  8

3.2 策略模式——用__call__实现可调用策略

# 传统策略模式需要定义接口和多个类
# 用__call__——每个策略就是一个可调用对象

class DiscountStrategy:
    """折扣策略基类"""
    def __call__(self, price):
        return price

class NoDiscount(DiscountStrategy):
    def __call__(self, price):
        return price

class PercentageDiscount(DiscountStrategy):
    def __init__(self, percent):
        self.percent = percent

    def __call__(self, price):
        return price * (1 - self.percent / 100)

class FixedDiscount(DiscountStrategy):
    def __init__(self, amount):
        self.amount = amount

    def __call__(self, price):
        return max(0, price - self.amount)

class ThresholdDiscount(DiscountStrategy):
    """满减策略:满threshold减amount"""
    def __init__(self, threshold, amount):
        self.threshold = threshold
        self.amount = amount

    def __call__(self, price):
        if price >= self.threshold:
            return price - self.amount
        return price

# 使用——策略即对象,对象即函数
def calculate_final_price(original_price, discount_strategy):
    """计算最终价格——接受任何可调用的策略"""
    final = discount_strategy(original_price)
    print(f"原价¥{original_price} → 折后¥{final}")
    return final

price = 500
calculate_final_price(price, NoDiscount())
calculate_final_price(price, PercentageDiscount(20))
calculate_final_price(price, FixedDiscount(80))
calculate_final_price(price, ThresholdDiscount(400, 100))

3.3 数据处理管道

# __call__非常适合构建可组合的数据处理器

class Pipeline:
    """数据处理管道——每个步骤是一个可调用对象"""
    def __init__(self):
        self.steps = []

    def add(self, processor):
        self.steps.append(processor)
        return self  # 链式添加

    def __call__(self, data):
        """执行管道——将数据依次通过每个处理器"""
        result = data
        for step in self.steps:
            result = step(result)
        return result

class RemoveNone:
    def __call__(self, data):
        return [x for x in data if x is not None]

class ToInt:
    def __call__(self, data):
        return [int(x) for x in data]

class FilterPositive:
    def __call__(self, data):
        return [x for x in data if x > 0]

class MultiplyBy:
    def __init__(self, factor):
        self.factor = factor

    def __call__(self, data):
        return [x * self.factor for x in data]

# 构建管道
pipeline = (Pipeline()
    .add(RemoveNone())
    .add(ToInt())
    .add(FilterPositive())
    .add(MultiplyBy(2))
)

# 使用
raw = ["5", None, "-3", "10", None, "0", "8"]
result = pipeline(raw)
print(f"原始: {raw}")
print(f"处理后: {result}")  # [10, 20, 16]

四、callvs 普通方法

# 什么时候用__call__,什么时候用普通方法?

# ✅ 用__call__:对象的"主要职责"就是做一件事
# 对象本质上是一个函数——只是带了配置/状态

class PasswordHasher:
    """密码哈希器——主要职责就是哈希密码"""
    def __init__(self, algorithm="sha256", iterations=100000):
        self.algorithm = algorithm
        self.iterations = iterations

    def __call__(self, password):
        """直接调用对象来哈希密码"""
        import hashlib
        data = password.encode()
        for _ in range(self.iterations):
            data = hashlib.new(self.algorithm, data).digest()
        return data.hex()

hasher = PasswordHasher()
result = hasher("my_password")  # 像函数一样调用
print(result[:20] + "...")

# ✅ 用普通方法:对象有多个职责,调用只是其中一种操作

class UserManager:
    """用户管理器——有多种操作,不适合__call__"""
    def create_user(self, name, email):
        pass

    def delete_user(self, user_id):
        pass

    def find_user(self, email):
        pass

# 💡 选型标准:
# - 对象=一件事 → __call__
# - 对象=多件事 → 普通方法

五、总结

__call__让对象获得了"可调用"的身份。最有价值的应用是创建有状态的函数——装饰器、策略、处理器管道。

核心要点:

  1. obj()调用的就是obj.__call__()
  2. callable(obj)检查是否定义了__call__
  3. 类装饰器最常用__call__——记住调用次数等状态
  4. 策略模式——每种策略是一个带__call__的对象
  5. 管道处理——chain多个__call__处理器

一句话:如果你发现自己在写"先配置一个对象,然后用它来执行主要功能"——__call__可能比普通方法更自然。

以上就是Python使用_call_让实例对象可调用的方法的详细内容,更多关于Python _call_实例对象可调用的资料请关注脚本之家其它相关文章!

相关文章

  • 深入解析神经网络从原理到实现

    深入解析神经网络从原理到实现

    这篇文章主要介绍了深入解析神经网络从原理到实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • Python利用prettytable实现格式化输出内容

    Python利用prettytable实现格式化输出内容

    Python有一个第三方模块叫 prettytable,专门用来将数据格式输出。本文将通过示例为大家详细讲讲prettytable的用法,感兴趣的可以了解一下
    2022-07-07
  • Python获取时光网电影数据的实例代码

    Python获取时光网电影数据的实例代码

    这篇文章主要介绍了Python获取时光网电影数据,基本原理是先通过requests库,通过时光网自带的电影数据API接口,获取到指定的电影数据,本文结合示例代码给大家介绍的非常详细,需要的朋友可以参考下
    2022-09-09
  • 用python给自己做一款小说阅读器过程详解

    用python给自己做一款小说阅读器过程详解

    这篇文章主要介绍了用python给自己做一款小说阅读器过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • numpy库与pandas库axis=0,axis= 1轴的用法详解

    numpy库与pandas库axis=0,axis= 1轴的用法详解

    这篇文章主要介绍了numpy库与pandas库axis=0,axis= 1轴的用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • Python3 tkinter 实现文件读取及保存功能

    Python3 tkinter 实现文件读取及保存功能

    tkinter是一个跨平台的GUI库,开发的程序可以在win,linux或者mac下运行,tkinter是python自带的GUI库,是对图形库TK的封装。本文通过实例代码给大家介绍Python3 tkinter 实现文件读取及保存功能,感兴趣的朋友跟随小编一起看看吧
    2019-09-09
  • Pycharm 操作Django Model的简单运用方法

    Pycharm 操作Django Model的简单运用方法

    今天小编就为大家分享一篇Pycharm 操作Django Model的简单运用方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-05-05
  • Python Pytorch深度学习之自动微分

    Python Pytorch深度学习之自动微分

    今天小编就为大家分享一篇关于Pytorch自动微分的文章,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-10-10
  • Python爬虫实现“盗取”微信好友信息的方法分析

    Python爬虫实现“盗取”微信好友信息的方法分析

    这篇文章主要介绍了Python爬虫实现“盗取”微信好友信息的方法,结合实例形式分析了Python针对微信数据信息爬取的相关操作技巧,需要的朋友可以参考下
    2019-09-09
  • Python+OpenCV实现单个圆形孔和针检测

    Python+OpenCV实现单个圆形孔和针检测

    这篇文章主要为大家详细介绍了如何通过Python+OpenCV实现单个圆形孔和针检测功能,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2022-10-10

最新评论