欢迎光临
我们一直在努力

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__让对象获得了"可调用"的身份。最有价值的应用是创建有状态的函数——装饰器、策略、处理器管道。

💡 核心要点:

  • obj()调用的就是obj.__call__()
  • callable(obj)检查是否定义了__call__
  • 类装饰器最常用__call__——记住调用次数等状态
  • 策略模式——每种策略是一个带__call__的对象
  • 管道处理——chain多个__call__处理器
  • ✅ 一句话:如果你发现自己在写"先配置一个对象,然后用它来执行主要功能"——__call__可能比普通方法更自然。

    赞(0)
    未经允许不得转载:171主机测评 » Python面向对象:__call__让实例对象可调用的方法
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址