用Python装饰整个库
我对装潢师的想法还不太熟悉(我仍在试图对他们进行思考),但我认为我遇到了一个非常适合他们的问题。我想要一个在数学库中所有函数上都有修饰的类。更具体地说,我的类有两个成员,x和flag。当flag为true时,我希望调用原始的数学函数。当标志为false时,我想返回无 作为我要问的问题的一个框架,这里有一节课:用Python装饰整个库,python,decorator,Python,Decorator,我对装潢师的想法还不太熟悉(我仍在试图对他们进行思考),但我认为我遇到了一个非常适合他们的问题。我想要一个在数学库中所有函数上都有修饰的类。更具体地说,我的类有两个成员,x和flag。当flag为true时,我希望调用原始的数学函数。当标志为false时,我想返回无 作为我要问的问题的一个框架,这里有一节课: import math class num(object): def __init__(self, x, flag): self.x = x self.flag =
import math
class num(object):
def __init__(self, x, flag):
self.x = x
self.flag = flag
def __float__(self):
return float(self.x)
因此,这样做很好:
a = num(3, True)
print math.sqrt(a)
然而,这应该(在我的完美世界中)返回None:
b = num(4, False)
print math.sqrt(b)
关于如何将此应用于整个函数库的任何建议或提示?您可以为此使用decorator,尽管您不需要使用
@decorator
语法
下面的代码将列出的每个函数从math
模块导入当前模块的名称空间,并将其包装在定义的装饰器中。它应该给你一个基本的想法
from functools import wraps
def check_flag(func):
@wraps(func)
def _exec(x, *args, **kw):
if getattr(x, 'flag', False):
return None
return func(x, *args, **kw)
return _exec
import sys, math
_module = sys.modules[__name__]
for func in ('exp', 'log', 'sqrt'):
setattr(_module, func, check_flag(getattr(math, func)))
正如Alex演示的那样,您可以自动列出
数学
模块中定义的函数,但我认为明确包装您感兴趣的函数是一种更好的方法 这里是总体思路…:
>>> class num(object):
... def __init__(self, x, flag):
... self.x = x
... self.flag = flag
... def __float__(self):
... return float(self.x)
... from functools import wraps
>>> def wrapper(f):
... @wraps(f)
... def wrapped(*a):
... if not all(getattr(x, 'flag', True) for x in a):
... return None
... return f(*(getattr(x, 'x', x) for x in a))
... return wrapped
...
>>> import inspect
>>> import math
>>> for n, v in inspect.getmembers(math, inspect.isroutine):
... setattr(math, n, wrapper(v))
...
>>> a = num(3, True)
>>> print math.sqrt(a)
1.73205080757
>>> b = num(4, False)
>>> print math.sqrt(b)
None
请注意,此包装还包括math
中的非一元函数(如果任何参数具有False
.flag
),则返回None
),并允许对其进行混合调用(一些参数是num
的实例,其他参数是实际浮点)
适用于任何“将所有函数包装在某个模块中”任务的关键部分是使用模块
inspect
获取模块math
中函数(内置或非内置)的所有名称和值,以及对包装器的显式调用(语义与decorator语法相同)要将该名称设置为math
模块中的包装值。您意识到可以直接从Python中的float
继承:class my_float(float):pass
我认为您的答案使用了不适当的duck类型。在一个循环中,任何具有名为flag
的成员的对象都被假定为num
,而在另一个循环中,任何包含名为x
的成员的对象都被假定为num
。@Omnifarious,将任何其他类型的对象传递给math的函数都是错误的,因此我通过包装扩展math的函数,要同时接受具有这些属性的对象,类型检查将成为障碍。我还认为您的答案使用了不适当的duck类型。您应该使用isinstance
,然后只需获取标志的值即可。否则,传入的任何成员名为flag
的内容都会被视为num
。如果您向这些函数传递了大量无效输入,并且依赖函数拒绝这些输入,则您的代码会遇到更深层次的问题。感谢您的回答,通过学习你的代码和Alex的答案,我学到了很多。我只接受他的答案而不接受你的答案,因为它给了我一种按照要求包装整个库(带有inspect)的方法。不过,我可以从包装所需的函数中看出逻辑。再次感谢!