python装饰器

  装饰器,顾名思义,是用来装饰某样东西的。那么它是用来装饰什么东东的呢?答案是函数。总结一下,装饰器就是修改其他函数某些功能的函数。

  接下来,让我们一步一步来编写一个装饰器。

  一、什么是函数

#在这里myfunc是一个变量,也是一个函数。二myfunc()则是一个函数调用
def myfunc(args = "Python"):
	return "Hello " + args
	
myval = myfunc#声明一个变量将myfunc赋值给这个新变量,实际上myfunc是函数的地址(注意当前没有小括号),myval变量保存的是myfunc函数的地址

print(myval,myfunc)#当前输出的内容是相同的,即myfunc函数的地址

del myfunc#将原来的myfunc变量删除,之后myfunc变量将不可见,也就不能再调用myfunc函数了

print(myval,myval())#输出myval变量的值,并调用该函数

print(myfunc)#会报错,因为该变量已被删除
print(myfunc())#如果没有上面的输出语句,同样会报错,因为myfunc变量被删除,也就不涉及到调用myfunc函数了

 输出结果如下:

 二、在函数中定义函数

def myfunc(args = "Python"):
	print("Hello "+args)
	def insideFunc():
		return "i am the inside function"
		
	print(insideFunc())
	print("now i am the outside function")
	

myfunc()
insideFunc()

 输出结果如下:

三、从函数中返回函数

def myfunc(args = "Python"):
	print("Hello "+args)
	def insideFunc():
		return "i am the inside function"
		
	print(insideFunc())
	print("now i am the outside function")
	
	return insideFunc;#注意:该出返回没有小括号,否则,该处将是返回insideFunc函数执行后的结果
	

func = myfunc();
print(func,func())

 输出结果如下:

四、将函数作为参数传递给另一个函数

def myfunc(args = "Python"):
	print("Hello "+args)
	return "funny"
	
def doSomething(func):
	print("now ,in function doSomething")
	print(func())


doSomething(myfunc)

  输出结果如下:

 五、应用以上只是,编写第一个装饰器函数

def myDecrator(func):
	def insideFunc():
		print("before func out")
		func()
		print("after func out")
		
	return insideFunc;
	

def myfunc(args = "python"):
	print("Hello "+args)
	

myfunc = myDecrator(myfunc)
myfunc()
	

  

 输出结果如下:

六、使用@来运行之前的代码

def myDecrator(func):
	def insideFunc():
		print("before func out")
		func()
		print("after func out")
		
	return insideFunc;
	

@myDecrator
def myfunc(args = "python"):
	print("Hello "+args)
	
myfunc()
print("Over")
	

  输出结果如下:

 

七、存在的小问题

 

def myDecrator(func):
	def insideFunc():
		print("before func out")
		func()
		print("after func out")
		
	return insideFunc;
	

@myDecrator
def myfunc(args = "python"):
	print("Hello "+args)
	
myfunc()

print(myfunc.__name__)
	

 当我们试图输出myfunc.__name__时,输出的名称为insideFunc,如下:

  这里的函数myfunc被insideFunc替代了。它重写了我们函数的名字和注释文档。幸运的是Python提供给我们一个简单的函数来解决这个问题,那就是functools.wraps。我们修改上一个例子来使用functools.wraps:

from functools import wraps

def myDecrator(func):
	@wraps(func)
	def insideFunc():
		print("before func out")
		func()
		print("after func out")
		
	return insideFunc;
	

@myDecrator
def myfunc(args = "python"):
	print("Hello "+args)
	
myfunc()

print(myfunc.__name__)
	

  输出结果如下:

现在看起来舒服多了。

八、蓝本规范

from functools import wraps
def decorator_name(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        if not can_run:
            return "Function will not run"
        return f(*args, **kwargs)
    return decorated
 
@decorator_name
def func():
    return("Function is running")
 
can_run = True
print(func())
# Output: Function is running
 
can_run = False
print(func())
# Output: Function will not run

  注意:@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。

使用场景

现在我们来看一下装饰器在哪些地方特别耀眼,以及使用它可以让一些事情管理起来变得更简单。

授权(Authorization)

装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。这里是一个例子来使用基于装饰器的授权:

from functools import wraps
 
def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            authenticate()
        return f(*args, **kwargs)
    return decorated

  

日志(Logging)

日志是装饰器运用的另一个亮点。这是个例子:

from functools import wraps
 
def logit(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print(func.__name__ + " was called")
        return func(*args, **kwargs)
    return with_logging
 
@logit
def addition_func(x):
   """Do some math."""
   return x + x
 
 
result = addition_func(4)
# Output: addition_func was called

  

带参数的装饰器

来想想这个问题,难道@wraps不也是个装饰器吗?但是,它接收一个参数,就像任何普通的函数能做的那样。那么,为什么我们不也那样做呢? 这是因为,当你使用@my_decorator语法时,你是在应用一个以单个函数作为参数的一个包裹函数。记住,Python里每个东西都是一个对象,而且这包括函数!记住了这些,我们可以编写一下能返回一个包裹函数的函数。

在函数中嵌入装饰器

我们回到日志的例子,并创建一个包裹函数,能让我们指定一个用于输出的日志文件。

from functools import wraps
 
def logit(logfile='out.log'):
    def logging_decorator(func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # 打开logfile,并写入内容
            with open(logfile, 'a') as opened_file:
                # 现在将日志打到指定的logfile
                opened_file.write(log_string + '
')
            return func(*args, **kwargs)
        return wrapped_function
    return logging_decorator
 
@logit()
def myfunc1():
    pass
 
myfunc1()
# Output: myfunc1 was called
# 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串
 
@logit(logfile='func2.log')
def myfunc2():
    pass
 
myfunc2()
# Output: myfunc2 was called
# 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串

  

装饰器类

现在我们有了能用于正式环境的logit装饰器,但当我们的应用的某些部分还比较脆弱时,异常也许是需要更紧急关注的事情。比方说有时你只想打日志到一个文件。而有时你想把引起你注意的问题发送到一个email,同时也保留日志,留个记录。这是一个使用继承的场景,但目前为止我们只看到过用来构建装饰器的函数。

幸运的是,类也可以用来构建装饰器。那我们现在以一个类而不是一个函数的方式,来重新构建logit。

from functools import wraps
 
class logit(object):
    def __init__(self, logfile='out.log'):
        self.logfile = logfile
 
    def __call__(self, func):
        @wraps(func)
        def wrapped_function(*args, **kwargs):
            log_string = func.__name__ + " was called"
            print(log_string)
            # 打开logfile并写入
            with open(self.logfile, 'a') as opened_file:
                # 现在将日志打到指定的文件
                opened_file.write(log_string + '
')
            # 现在,发送一个通知
            self.notify()
            return func(*args, **kwargs)
        return wrapped_function
 
    def notify(self):
        # logit只打日志,不做别的
        pass

  这个实现有一个附加优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法:

@logit()
def myfunc1():
    pass

  现在,我们给 logit 创建子类,来添加 email 的功能(虽然 email 这个话题不会在这里展开)。

class email_logit(logit):
    '''
    一个logit的实现版本,可以在函数调用时发送email给管理员
    '''
    def __init__(self, email='admin@myproject.com', *args, **kwargs):
        self.email = email
        super(email_logit, self).__init__(*args, **kwargs)
 
    def notify(self):
        # 发送一封email到self.email
        # 这里就不做实现了
        pass

  

注:以上内容参考自https://www.runoob.com/w3cnote/python-func-decorators.html

原文地址:https://www.cnblogs.com/rabbit0212/p/11214788.html