装饰器

阅读量: 鲁文奎 2021-04-22 12:04:24
Categories: Tags:

装饰器

创建“Well-Behaved”装饰器

其主要提供了 对带装饰器方法的名称 与 方法原名称保持一致 的 功能
类似于functools 中提供的 @wrapper 功能
如下, 我们可以直接获取带装饰器修饰的方法的 名称:

代码:

def my_simple_logging_decorator(func):
	def you_will_never_see_this_name(*args,**kwargs):
		print "calling {}".format(func.__name__)
		return func(*args,**kwargs)
	return you_will_never_see_this_name


@my_simple_logging_decorator
def double(x):
	'Doubles a number.'
	return 2 * x
	
print double.__name__

输出:

>>> you_will_never_see_this_name

如上 ,当我们调用double.__name__的时候, 输出的 并不是 我们 直接看到的double,而是被装饰器修饰之后的方法名
如果我们有需求 获取 方法的原名字, 可以按照如下方式来操作(如果想自己写的话, 当然也可以直接import functools)

代码分析:

def simple_decorator(decorator):
	def new_decorator(f):
		g = decorator(f)
		g.__name__ = f.__name__
		g.__doc__ = f.__doc__
		g.__dict__.update(f.__dict__)
		return g
	new_decorator.__name__ = decorator.__name__
	new_decorator.__doc__ = decorator.__doc__
	new_decorator.__dict__.update(decorator.__dict__)
	return new_decorator

@simple_decorator
def my_simple_logging_decorator(func):
	def you_will_never_see_this_name(*args,**kwargs):
		print "calling {}".format(func.__name__)
		return func(*args,**kwargs)
	return you_will_never_see_this_name


@my_simple_logging_decorator
def double(x):
	'Doubles a number.'
	return 2 * x

assert double.__name__ == 'double'
assert double.__doc__ == 'Doubles a number.'
print double(155)

简单分析一下上述代码:()
如果想达成double.name == 'double' 这个目的, 其实我们只需要修改you_will_never_see_this_name方法的名称即可
这里肯定不能直接将you_will_never_see_this_name的名字改成double的, 因为装饰器修饰另外一个方法, 通过XXX.name 也应该返回另外一个方法的名字,
这才是我们想要的效果. 直接hardcode是无法做到的.

因为装饰器的行为是发生在编译时, 所以装饰器的内容是最先调用的.
从上到下, 上下文 会先 找到 @simple_decorator, 然后 跳转到def simple_decorator,
执行simple_decorator(my_simple_logging_decorator),
然后就会发现 def simple_decorator 中的 new_decorator 的名字被修改为 my_simple_logging_decorator,然后返回 new_decorator
此时 new_decorator = my_simple_logging_decorator
然后,上下文 会接着往下发现@my_simple_logging_decorator, 然后 跳转到def my_simple_logging_decorator,
但是 根据前面的处理, 此时 my_simple_logging_decorator 被装饰器修饰了, 调用my_simple_logging_decorator方法, 需要先处理装饰器部分.
可知, my_simple_logging_decorator 的装饰器 simple_decorator 返回了 new_decorator, 所以实际上是这么个过程:
new_decorator(double)
然后 执行这个方法, 发现 g = decorator(f) 这个 会调用 my_simple_logging_decorator方法, 返回 you_will_never_see_this_name
也就是:
g = you_will_never_see_this_name
而 you_will_never_see_this_name 以double 作为参数, 返回的 是 double函数的调用结果
所以, 修改了 g 的__name__, 其实 也就是修改了 you_will_never_see_this_name, 也就达成了我们的目的