不过,使用可选参数制作包装器的简单实用工具
biwrap的Python项目详细描述
不过,使用简单的实用工具制作带有可选参数的包装器
安装
主分支
pip install git+https://github.com/ferrine/biwrap
最新版本
pip install biwrap
概述的问题
可读性和透明实现非常重要。包装器是编程中的高级主题,在某些情况下使它们可读是困难的。特殊情况是参数化包装。可以使用参数调用它,也可以不使用参数调用它。处理这种情况的实现通常很复杂,看起来很奇怪(请参见SO thread)。这个包解决了这个问题,并提供了简单通用的解决方案。
API
将其应用于问题的最小片段。
importbiwrap@biwrap.biwrapdefwrapper(fn,arg1='default',arg2='default'):defwrapped(*fn_args,**fn_kwargs):...# depends on `arg1`, `arg2`returnwrapped@wrapper# use defaultsdeffunc1(a,b):...@wrapper(arg1='non default')# change defaultsdeffunc2(a,b):...
示例
让我们讨论一个需要将函数放入注册表的情况。有些函数可以有别名。
天真的解决方案
defregister(alias=None):definner(fn):iffn.__name__notinregister.storage:register.storage[fn.__name__]=fnelifregister.storage[fn.__name__]isnotfn:raiseKeyError('{} is already in storage'.format(fn.__name__))ifaliasisnotNoneandaliasnotinregister.storage:register.storage[alias]=fnelifaliasisnotNone:raiseKeyError('{} is already in storage'.format(alias))returnfnreturninnerregister.storage={}@register()deff1(a):returnaprint(register.storage)#> {'f1': <function f1 at 0x11ff519d8>}@register(alias='fn3')deff2(a):returnaprint(register.storage)#> {'f1': <function f1 at 0x11ff519d8>, 'f2': <function f2 at 0x10a87d0d0>, 'fn3': <function f2 at 0x10a87d0d0>}
分析
上面的示例在
- decorator定义具有双嵌套(doubledef)
- 用法需要后面的括号@register(),即使我们不使用可选参数
更具可读性的代码应该避免这两点,并且看起来像:
defregister(fn,alias=None):...@registerdeff1(a):returna@register(alias='fn3')# <- (1)deff2(a):returna
上述api的简单实现将不起作用。上面标记为(1)的行将失败,因为未传递第一个参数fn。但我们希望输出是一样的。
更好的解决方案
importbiwrap@biwrap.biwrapdefregister(fn,alias=None):iffn.__name__notinregister.storage:register.storage[fn.__name__]=fnelifregister.storage[fn.__name__]isnotfn:raiseKeyError('{} is already in storage'.format(fn.__name__))ifaliasisnotNoneandaliasnotinregister.storage:register.storage[alias]=fnelifaliasisnotNone:raiseKeyError('{} is already in storage'.format(alias))returnfnregister.storage={}@registerdeff1(a):returnaprint(register.storage)#> {'f1': <function f1 at 0x10f45a048>}@register(alias='fn3')deff2(a):returnaprint(register.storage)#> {'f1': <function f1 at 0x10f45a048>, 'f2': <function f2 at 0x10f45a488>, 'fn3': <function f2 at 0x10f45a488>}
功能概述
可能存在一些角情况,自定义编码可以为每个用例创建一个样板(见图SO thread)。这个包采用了最好的方法,实现了简单但通用的解决方案来解决所有问题。.
设置
以一个简单的包装器为例。它将根据参数化打印hi或bye,默认值为hi。
importbiwrap@biwrap.biwrapdefhiwrap(fn,hi=True):defnew(*args,**kwargs):ifhi:print('hi')else:print('bye')returnfn(*args,**kwargs)returnnew
病例
函数包装
定义的包装可以以两种方式使用
@hiwrapdeffn(n):print(n)fn(1)#> hi#> 1@hiwrap(hi=False)deffn(n):print(n)fn(1)#> bye#> 1
绑定方法包装器
biwrap也适用于绑定方法。如SO thread所示,这可能是一个问题,因为第一个位置参数是self,而不是函数。
classW:def__init__(self,n):self.n=n@biwrap.biwrapdefwrap(self,fn,text='hi'):defwrapped(*args,**kwargs):for_inrange(self.n):print(text)returnfn(*args,**kwargs)returnwrappedwr=W(3)@wr.wrapdeffn(n):print(n)fn(1)#> hi#> hi#> hi#> 1@wr.wrap(text='bye')deffn(n):print(n)wr.n=2fn(2)#> bye#> bye#> 2
类方法/属性包装
实现也处理这些情况
classO:def__init__(self,n):self.n=n@classmethod@hiwrapdeffn(cls,n):print(n)@property@hiwrap(hi=False)defnum(self):returnself.no=O(2)o.fn(1)#> hi#> 1print(o.num)#> bye#> 2
包装器作为函数
像call这样的函数也可以
deffn(n):print(n)fn=hiwrap(fn,hi=False)fn(1)#> bye#> 1