python-3.x - 如何使用带参数的python装饰器?
问题描述
我想定义一个装饰器,它将通过作为装饰器参数给出的名称注册类。我可以从 stackoverflow 和其他来源中阅读许多示例,这些示例展示了如何派生此类(棘手的)代码,但是当适应我的需要时,我的代码无法产生预期的结果。这是代码:
import functools
READERS = {}
def register(typ):
def decorator_register(kls):
@functools.wraps(kls)
def wrapper_register(*args, **kwargs):
READERS[typ] = kls
return wrapper_register
return decorator_register
@register(".pdb")
class PDBReader:
pass
@register(".gro")
class GromacsReader:
pass
print(READERS)
此代码生成一个空字典,而我希望字典有两个条目。你知道我的代码有什么问题吗?
解决方案
接受参数(通过(...)
)和装饰(通过@
)都会导致函数调用。接受参数或装饰的每个“阶段”都映射到一个调用,从而映射到装饰器定义中的一个嵌套函数。register
是一个三阶段的装饰器,需要尽可能多的调用来触发其最里面的代码。这些,
- 第一个是参数 (
(".pdb")
), - 第二个是类定义(
@... class
),和 - 第三个是类调用/实例化(
PDBReader(...)
)- 这个阶段被破坏了,因为它没有实例化类。
为了将类本身存储在字典中,将其存储在第二阶段。由于不存储实例,因此删除第三阶段。
def register(typ): # first stage: file extension
"""Create a decorator to register its target for the given `typ`"""
def decorator_register(kls): # second stage: Reader class
"""Decorator to register its target `kls` for the previously given `typ`"""
READERS[typ] = kls
return kls # <<< return class to preserve it
return decorator_register
请注意,装饰器的结果会替换其目标。因此,您通常应该返回目标本身或等效对象。由于在这种情况下类会立即返回,因此无需使用functools.wraps
.
READERS = {}
def register(typ): # first stage: file extension
"""Create a decorator to register its target for the given `typ`"""
def decorator_register(kls): # second stage: Reader class
"""Decorator to register its target `kls` for the previously given `typ`"""
READERS[typ] = kls
return kls # <<< return class to preserve it
return decorator_register
@register(".pdb")
class PDBReader:
pass
@register(".gro")
class GromacsReader:
pass
print(READERS) # {'.pdb': <class '__main__.PDBReader'>, '.gro': <class '__main__.GromacsReader'>}
推荐阅读
- javascript - 防止窗口默认滚动
- javascript - 如何获取过滤器选择的更新计数?
- python - Pandas 数据框 - 将 N 行从一个数据框移动到另一个数据框
- python - 如何将字符串数字转换为int并避免转换前的字符串文本
- reactjs - Redux store:如何订阅组件中的实际更改
- javascript - 方法的返回值没有出现
- logging - 如何在 gcp 上查看我的自然语言处理程序的活动日志/时间表
- docker - 有没有办法将 docker 映像复制到主机 docker 映像位置而不是创建存储库?
- android - FirebasePagingAdapter 是一次加载所有数据还是根据页面大小加载数据?
- html - 小型设备上轮播中的按钮封面图像