from functools import wraps
def keep_alive(func):
"""装饰器:为方法自动维护长连接"""
@wraps(func)
def wrapper(self, *args, **kwargs):
if not self.conn:
self.conn = ConnectHandler(**self.device_params)
self.conn.enable()
return func(self, *args, **kwargs)
return wrapper
class RouterClient:
def __init__(self, device_params):
self.device_params = device_params
self.conn = None
@keep_alive
def execute(self, command):
return self.conn.send_command(command)
def close(self):
if self.conn:
self.conn.disconnect()
# 使用
client = RouterClient(device)
client.execute("show run") # 自动创建连接
client.execute("show interfaces") # 复用连接
client.close() # 显式关闭
1. 如何给类中的方法添加装饰器?
在Python中,给类的方法添加装饰器的方式与普通函数完全一致,直接在方法定义前使用 @装饰器名
即可。例如,在代码中:
class RouterClient:
@keep_alive
def execute(self, command):
return self.conn.send_command(command)
这里的 @keep_alive
会作用于 execute
方法。关键在于装饰器的实现需要正确处理实例方法。因为实例方法的第一个参数是 self
,所以装饰器内部的包装函数 wrapper
必须接收 self
作为第一个参数:
def wrapper(self, *args, **kwargs): # 这里显式接收 self
if not self.conn:
self.conn = ConnectHandler(**self.device_params)
self.conn.enable()
return func(self, *args, **kwargs) # 传递 self 给原始方法
当 client.execute("show run")
被调用时,self
实际上是 RouterClient
的实例,因此装饰器可以正确访问 self.conn
。
2. @wraps(func)
的目的是什么?
@wraps(func)
是 functools
模块提供的一个装饰器,它的作用是保留被装饰函数(func
)的元数据(如 __name__
、__doc__
等)。如果不使用 @wraps
,被装饰后的函数会丢失原始函数的信息:
- 没有
@wraps
的情况:print(client.execute.__name__) # 输出 "wrapper"
- 使用
@wraps
的情况:print(client.execute.__name__) # 输出 "execute"
这对于调试、生成文档或依赖函数元数据的代码(如某些 Web 框架的路由装饰器)非常重要。@wraps(func)
确保装饰后的函数看起来更像原始函数。
3. keep_alive
中的 if not self.conn
语句,self.conn
是从哪里来的?
self.conn
是 RouterClient
类的实例属性。在 RouterClient
的 __init__
方法中,它被初始化为 None
:
class RouterClient:
def __init__(self, device_params):
self.device_params = device_params
self.conn = None # 初始化 conn 为 None
在装饰器的 wrapper
函数中,self
指向调用 execute
方法的 RouterClient
实例。例如:
client = RouterClient(device)
client.execute("show run") # 此时 self 是 client 实例
当第一次调用 execute
时,self.conn
是 None
,装饰器会创建连接并赋值给 self.conn
;后续调用时,self.conn
已存在,直接复用。
总结代码逻辑:
-
创建实例:
client = RouterClient(device)
client.conn
初始化为None
。
-
首次调用
execute
:client.execute("show run")
- 装饰器检查
self.conn
为None
,创建连接并赋值给self.conn
。 - 执行
send_command("show run")
。
- 装饰器检查
-
再次调用
execute
:client.execute("show interfaces")
self.conn
已存在,直接复用连接。- 执行
send_command("show interfaces")
。
-
显式关闭连接:
client.close()
- 调用
conn.disconnect()
关闭连接。
- 调用