星河耀银海头像
关注
Python面向对象:__call__让实例对象可调用的方法封面图

Python面向对象:__call__让实例对象可调用的方法

Python面向对象:__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]

四、call vs 普通方法

# 什么时候用__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__可能比普通方法更自然。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/aiclaws/article/details/164358809

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--