python3 闭包函数 装饰器

闭包函数

1.闭:定义在函数内部的函数 2.包:内部函数引用了外部函数作用域的名字

在函数编程中经常用到闭包。闭包是什么,它是怎么产生的及用来解决什么问题呢。给出字面的定义先:
闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数+引用环境)(想想Erlang的外层函数传入一个参数a, 内层函数依旧传入一个参数b, 内层函数使用a和b, 最后返回内层函数)
这个从字面上很难理解,特别对于一直使用命令式语言进行编程的程序员们。本文将结合实例代码进行解释。
函数是什么都知道:函数只是一段可执行代码,编译后就“固化”了,每个函数在内存中只有一份实例,得到函数的入口点便可以执行函数了。
在函数式编程语言中,函 数是一等公民(First class value:第一类对象,我们不需要像命令式语言中那样借助函数指针,委托操作函数),函数可以作为另一个函数的参数或返回值,可以赋给一个变量。
函数可 以嵌套定义,即在一个函数内部可以定义另一个函数,有了嵌套函数这种结构,便会产生闭包问题。如:# def outter():
# x = 111
# def inner():
# print(x)
# return inner
# res = outter() # res就是inner函数内存地址


# def func():
# x = 333
# res()
# func()

python中的闭包从表现形式上定义(解释)为:
如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure).
这个定义是相对直白的,好理解的,不像其他定义那样学究味道十足(那些学究味道重的解释,在对一个名词的解释过程中又充满了一堆让人抓狂的其他陌生名词,不适合初学者)。下面举一个简单的例子来说明。

# 给函数体传值的第一种方式 传参
# def index1(username):
# print(username)
#
# # 给函数体传参的第二种方式 闭包
# def outter(x,y):
# # x = 1
# # y = 40
# def my_max():
# if x > y:
# return x
# return y
# return my_max
# res1 = outter(1,40) # res就是my_max函数的内存地址
# print(res1())
# print(res1())
# print(res1())
# res2 = outter(90,200)
# print(res2())
# print(res2())
# print(res2())





import requests


# 第一个直接给函数传参
url1 = 'https://www.baidu.com'
url2 = '...'
def my_get(url):
response = requests.get(url)
if response.status_code == 200:
print(len(response.text))

my_get(url1)
my_get(url1)
my_get(url1)
my_get('https://www.baidu.com')
my_get('https://www.baidu.com')
my_get('https://www.baidu.com')





# 第二种给函数传参的方式 闭包
def outter(url):
# url = 'https://www.jd.com'
def my_get():
response = requests.get(url)
if response.status_code == 200:
print(len(response.text))
return my_get
my_jd = outter('https://www.jd.com')
my_jd()
my_jd()
my_baidu = outter('https://www.baidu.com')
my_baidu()
my_baidu()
my_baidu()
二,使用闭包注意事项

1,闭包中是不能修改外部作用域的局部变量的
除非事先声明globl nonlocal 变量类型
三,作用
说了这么多,不免有人要问,那这个闭包在实际的开发中有什么用呢?闭包主要是在函数式开发过程中使用。以下介绍两种闭包主要的用途。
 
用途1,当闭包执行完后,仍然能够保持住当前的运行环境。
用途2,闭包可以根据外部作用域的局部变量来得到不同的结果,这有点像一种类似配置功能的作用,我们可以修改外部的变量,闭包根据这个变量展现出不同的功能。比如有时我们需要对某些文件的特殊行进行分析,先要提取出这些特殊行。
 
 
 

装饰器:

器:就是一个工具
装饰:给被装饰对象添加新的功能

为什么要用装饰器
开放封闭原则:
开放:对扩展开放
封闭:对修改封闭

装饰器(可调用对象)必须遵循的两个原则:
1.不改变被装饰对象源代码
2.不改变被装饰对象(可调用对象)调用方式
def index():
pass
index()

如何用

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



这里我们写一个日志的装饰器
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

更进一步 我们可以 继续封装装饰器 如下

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

装饰器的执行顺序检测

from functools import wraps
def outter(func):
    @wraps(func)  # 装饰器修复技术
    def inner(*args,**kwargs):
        """
        我是inner函数
        :param args:
        :param kwargs:
        :return:
        """
        print('执行被装饰函数之前 你可以执行的操作')
        res = func(*args,**kwargs)
        print('执行被装饰函数之后 你可以执行的操作')
        return res
    return inner

@outter  # index = outter(最原始的index内存地址)
def index():
    """
    这是index函数
    :return:
    """
    pass


print(index)
print(help(index)) # 查看函数的注释
print(index.__name__) # 查看函数名字符串形式
index()
"""
用户查看被装饰函数的函数名的时候查看到的就是被装饰函数本身
用户查看被装饰函数的注释的时候查看到的就是被装饰函数的注释
"""

def outter1(func1):
    print('加载了outter1')
    def Decorators1(*args,**kwargs):
        print('执行了Decorators1')
        res1=func1(*args,**kwargs)
        print('执行了func1')
        return res1
    return Decorators1
def outter2(func2):
    print('加载了outter2')
    def Decorators2(*args,**kwargs):
        print('执行了Decorators2')
        res2=func2(*args,**kwargs)
        print('执行了func2')
        return res2
    return Decorators2
def outter3(func3):
    print('加载了outter3')
    def Decorators3(*args,**kwargs):
        print('执行了Decorators3')
        res3=func3(*args,**kwargs)
        print('执行了func3')
        return res3
    return Decorators3
@outter1  # index = outter1(wapper2)
@outter2  # Decorators2 = outter2(Decorators3)
@outter3  # Decorators3 = outter3(最原始的index函数内存地址)
def index():
    print('from index')

"""
加载了outter3
加载了outter2
加载了outter1

执行了Decorators1
执行了Decorators2
执行了Decorators3
from index
"""
index()

执行结果为:


加载了outter3
加载了outter2
加载了outter1
执行了Decorators1
执行了Decorators2
执行了Decorators3
from index
执行了func3
执行了func2
执行了func1

Process finished with e

原文地址:https://www.cnblogs.com/Sunbreaker/p/11171282.html