能否在调用被装饰函数时向Python装饰器传递参数?
如何在调用被装饰函数时传递装饰器的path参数
你的问题核心在于原来的装饰器在函数定义阶段(装饰时)就已经读取了指定path的文件,导致path参数被固定,无法在调用函数时动态修改。要解决这个问题,我们需要调整装饰器的逻辑,把文件读取操作移到函数调用阶段,同时允许在调用时传递path参数。
解决方案1:调整装饰器支持调用时传path
修改装饰器,让它在每次调用被装饰函数时才读取文件,并且从调用参数中获取path(如果没有则用默认值):
# 假设BATCH_PATH是你定义的默认路径常量 BATCH_PATH = "default_commands.txt" def read_commands(inner): def wrapper(*args, **kwargs): # 从kwargs中取出path,没有则用默认值 path = kwargs.pop('path', BATCH_PATH) # 每次调用时才读取文件,过滤空行避免无效命令 with open(path) as f: commands = ['python ' + line.rstrip('\n') for line in f if line.strip()] for command in commands: inner(command, *args, **kwargs) return wrapper
然后你的被装饰函数不需要修改,调用时直接传递path参数即可:
import os import logging import argparse LOG = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) @read_commands def execute_multi_commands(command, count): LOG.info(f'Executing command {count}: {command}') os.system(command) # 注意:这里的count是int类型(不可变),修改不会影响外部的count变量 count += 1 def main(): parser = argparse.ArgumentParser() parser.add_argument('-b', '--batch', action='store', type=str, dest='batch') args = parser.parse_args() count = 1 # 调用时传递path参数 execute_multi_commands(count, path=args.batch) if __name__ == "__main__": main()
额外优化:解决count不递增的问题
你可能会发现上面的代码中,每次打印的count都是1——这是因为int是不可变类型,函数内的count +=1只是创建了一个新的int对象,不会改变外部的count变量。解决这个问题可以用两种方式:
方式A:用可变类型存储count
把count放在列表里(列表是可变类型):
def main(): # ... count = [1] # 用列表保存count execute_multi_commands(count, path=args.batch) @read_commands def execute_multi_commands(command, count): LOG.info(f'Executing command {count[0]}: {command}') os.system(command) count[0] += 1
方式B:在装饰器中管理count
如果不想修改被装饰函数的参数,可以把count的管理移到装饰器里:
def read_commands(inner): def wrapper(start_count, **kwargs): path = kwargs.pop('path', BATCH_PATH) with open(path) as f: commands = ['python ' + line.rstrip('\n') for line in f if line.strip()] current_count = start_count for command in commands: inner(command, current_count) current_count += 1 return wrapper # 调用时只传起始count execute_multi_commands(1, path=args.batch)
解决方案2:带默认参数的装饰器工厂
如果你希望在装饰函数时就能指定默认path,同时保留调用时覆盖的能力,可以把装饰器改成带参数的工厂模式:
def read_commands(default_path=BATCH_PATH): def decorator(inner): def wrapper(*args, **kwargs): # 优先使用调用时传入的path,没有则用装饰时的默认值 path = kwargs.pop('path', default_path) with open(path) as f: commands = ['python ' + line.rstrip('\n') for line in f if line.strip()] for command in commands: inner(command, *args, **kwargs) return wrapper return decorator
装饰函数时可以指定默认path:
@read_commands(default_path="/my/default/commands.txt") def execute_multi_commands(command, count): LOG.info(f'Executing command {count}: {command}') os.system(command) count += 1
调用时依然可以通过path=args.batch覆盖默认值,和之前的调用方式一致。
内容的提问来源于stack exchange,提问作者Colin
相关产品推荐
相关产品推荐

