当你发现自己是周遭一群人中最聪明的那一个,说明你该换个圈子了
by Volsky Surd(沃兹各 硕德)
注意:本篇内容较为深奥,不建议使用Python经验较少的用户阅读
当你阅读了文章标题可能会感到困惑,本文讲述的内容涉及什么样的场景。本文涉及的场景比较罕见,简单地说,就是你要定义一个装饰器,这个装饰器需要在运行时提供调整自身行为的接口。如果你还没懂的话,不必纠结在此处,继续阅读下去就好了。
我在此处设定了一个具体场景,你可能不知道,Python在某些情况之下,进行哪怕最简单的数学运算也可能出现错误。比如你手头有电脑的话可以试试2.2+0.2。不同机器运行的结果不知道会不会有差异,在我的机器上(openSUSE leap 15.2 CPython3.7.7)结果如下
如何规避这个问题呢,简单来说,你并不一定非得规避这个问题,除非你是干金融之类的,差一分钱都不行那种。属实要进行精确运算的话,可以使用decimal库,没记错的话,这是Python标准库之一。但是此库使用的话会降低程序运行效率,我相信一般行业不会想为了提高这么点精度而选择拖慢程序速度。更具体的东西本文不涉及,接下来我搞一个可以指定计算精度高低的装饰器,直接上代码。
from decimal import Decimalfrom functools import wraps, partialdef add_attr_wrapper(obj, func=None): """装饰器,用于为obj添加一个函数func作为属性""" if func is None: return partial(add_attr_wrapper, obj) setattr(obj, func.__name__, func) return funcdef calc_precision(func=None, precision="high"): """指定数学运算的精度""" if func is None: return partial(calc_precision, precision=precision) print(f"正在执行{func.__name__}函数") @wraps(func) def inner(*args, **kwargs): print(f"将以{precision}精度计算") if precision == "high": # 如果指定了高精度运算,则使用decimal运算 # 只需此前将常规浮点数转换成decimal中的数 args = [Decimal(str(arg)) for arg in args] res = func(*args, **kwargs) return float(res) # 将运算结果转换成常规浮点数,方便使用 @add_attr_wrapper(inner) def set_precision(new_precision): # 精髓在这,为装饰后的对象添加这个接口,可以在程序运行时改变计算精度 nonlocal precision precision = new_precision return inner
有了这个,下一步就是将装饰器应用在函数上
@calc_precision(precision="norm") # 此处指定默认以常规精度运算def add(x, y): return x + yif __name__ == "__main__": result = add(2.2, .2) print(result) add.set_precision("high") # 此时改以高精度运算 result = add(2.2, .2) print(result) add.set_precision("norm") result = add(2.2, .2) print(result)
我们可以用用看效果
下面我来做一点简单讲解,如果没有29-33(作为文科生,提到这两个数马上想起了经济大萧条)行代码,这里calc_precision只是一个普通的装饰器,29-33行添加了运行时修改内部设定的行为,这里将precision属性做了修改。而29-33行可以生效要依靠5-10行的add_attr_wrapper装饰器,将可以修改calc_precision内部设定的set_precision函数开放给用户访问,妙啊。
如果看不懂没有关系,这类使用场景相当罕见,就连笔者自己都没有遇见过。我不得不承认,如果遇见了的话也得把这篇文章翻出来扒代码使,这种事情当你知道可以从哪里直接拿现成的东西来用的话,就没有必要记住它,爱因斯坦连音速多少都记不得,他说
“[I do not] carry such information in my mind since it is readily available in books.”,我完全认同。