定义 本质就是函数,功能是装饰其他函数,其实就是给其他函数添加附加功能, 原则 1、不能修改被装饰的函数的源代码 2、不能修改被装饰的函数的调用方式 其实就是当你用装饰器函数装饰其他函数的时候,被装饰的函数被装饰之前的调用方式不会有任何区别 实现装饰器的知识储备 1、函数即“变量” 2、高阶函数 3、嵌套函数 高阶函数+嵌套函数===装饰器 关于函数即变量的理解 上图中描述了函数在内存中存放的原理图,其实test函数名就相当于函数在内存中存放的一个地址,也就是图中所说的房间门牌号 高阶函数 什么是高阶函数? 1、把一个函数名当做实参传给另外一个函数 2、返回值中包含函数名 写一个高阶函数的例子:
上面的代码的例子就是一个高阶函数,满足了把函数名当做实参传给另外一个函数,上述代码中是把bar函数名传递给了test1作为实参,这里就起到了一个装饰的作用,相当于给bar函数增加了一个计算运行时间的功能,但是不能称为装饰器。因为上述我们的原则中说了,装饰器不能修改被装饰器函数的调用方式,我们这里的bar函数调用方式变了,变成了test1(bar),而不是bar(),并且我们的高阶函数还有一个条件没有满足,就是“返回值中包含函数名” 我们接着再写一个高阶函数的例子:
关于上述代码的一个分析: 当执行test2(bar)的时候,最后返回的其实就是bar函数内存地址,也就是我们所说的门牌号,所以当我们再次给他赋值给bar的时候,执行bar()就会执行bar函数 嵌套函数 嵌套函数就是在一个函数体内容再用def 定义一个函数,例子如下:
上述讲完了关于装饰器的知识储备: 函数即变量 高阶函数 嵌套函数 正式开始理解装饰器 高阶函数+嵌套函数===装饰器 我们写一个例子:
上述代码就是一个简单的装饰器, 通过下面图解对装饰器工作的原理的一个理解: 但是上面代码中的使用装饰器的方法并不是最好的,因为我们是执行了test1=timer(test1)然后执行test1(),其实python中给我们提供了一种方法,即要装饰的函数上面添加@timer,执行@timer其实就是还行test1 = timer(test1) ,并且我们的这种方式,还存在一个问题,就是当被装饰的函数有参数需要传递的时候,就不能用了,所以我们修改后的代码为:
但是上面这种代码还不是最完整的,有一种情况上述的代码无法使用,就是在做用户认证的时候,如果有多个系统,需要不同的认证方式,注意:上述代码还有一个小问题就是如果被装饰的函数存在return返回值得时候,也是无法显示返回的数据,即代码应该为:
通过图解理解上面的代码: 所以整个程序执行的过程如下: @auth(auth_type=”local”)----->auth(auth_type=”local”)----->因为auth(auth_type=”local”)就相当执行auth函数,所以执行结果为outer_wapper,即转换为 @outer_wapper----->@outer_wapper相当于bbs=outer_wapper(bbs)----->执行outer_wapper(bbs)的结果为wapper,所以bbs=wapper,也就是说bbs这个时候相当于wapper函数名的内存地址----->当执行bbs() 就相当于执行了wrapper() 在看闭包问题之前先来看看关于python中作用域的问题 变量作用域  对于上述代码中出现错误,肯定没什么疑问了,毕竟b并没有定义和赋值,当我们把代码更改如下后:  再看一个例子:  首先这个错误已经非常明显:说在赋值之前引用了局部变量b 可能很多人觉得会打印10然后打印6,其实这里就是涉及到变量作用域的问题 当Python编译函数的的定义体的时候,它判断b是局部变量,毕竟在函数中有b = 9表示给b赋值了,所以python会从本地环境获取b,当我们调用方法执行的时候,定义体会获取并打印变量a的值,但是当尝试获取b的值的时候发现b没有绑定值,所以要想让上述代码运行还可以把b设置为全局变量,或者把b赋值放到调用之前   函数对象的作用域 python中一切皆对象,同其他对象一样,函数对象也有其使用的范围即函数对象的作用域。 在python中我们通过def定义函数,函数对象的作用域与def所在的层级相同, 通过下面代码进行理解: def func1(): def func2(x): return 2*x print(func2(5))func1()print(func2(5)) 这个例子中我们在def func1函数内可以调用fun2,但是我们在外面是无法调用到func2的,所以结果为看到如下:  闭包 关于闭包主要有下面两种说法:
个人觉得第二种说法更准确,闭包只是在形式上表现像函数,实际不是函数。 我们对函数的定义是:一些可执行的代码,这些代码在函数定义后就确定了,不会在执行时发生变化,所以一个函数只有一个实例。 闭包在运行的时候可以有多个实例,不同的引用环境和相同的环境组合可以产生不同的实例。 这里有一个词:引用环境,其实引用环境就是在执行运行的某个时间点,所有处于活跃状态的变量所组成的集合,这里的变量是指变量的名字和其所代表的对象之间的联系。 可以使用闭包语言的特点:
而认为闭包是函数的有一句话是: 闭包是指延伸了作用域的函数,其中包含函数定义体中引用。但是不在定义体中定义的非全局变量。 上面这种说法个人觉得也是一种理解方式 相信看了这些概念也还是不好理解,还是通过下面例子更好理解: 先实现一种计算平均值的方法:  从结果我们可以看出这里保存了每次的历史值 换一种方法实现:  实现了第一种相同的效果,对这种方法分析: 通常我们会认为我们调用avg(10)的时候make_averager函数已经返回了,而它的本地作用域也一去不复返,但这里其实series是自由变量,是指未在本地作用域绑定的变量 我们可以通过print(dir(avg)),看到如下结果:  其实这里面保存着均布变量和自由变量的名称,我们可以通过下面方法查看:  series的绑定在返回的avg函数的__closure__属性中这或许就是有的人会认为闭包一种函数。闭包会保留定义函数时存在的自由变量的绑定,这样调用函数时虽然定义作用域不能用了,但是仍能使用那些绑定 关于nonlocal 刚开始了解闭包之后,如果尝试使用这种编程方式容易出现以下错误使用例子: def make_averager(): count = 0 total = 0 def averager(new_value): count += 1 total += new_value return total / count return averager 先来看一下错误提示:  这个例子中和我们上面使用的不同之处是:这里的count和total是数字,是不可变类型,而之前的例子中series是一个列表是可变类型 所以这里重新回到了最开始说的作用域问题了,当我们在averager中使用 count += 1的时候其实就是count = count + 1,这样就是在averager函数定义体中对count进行赋值,count就变成了局部变量。 问题小结:当时数字,字符串,元组等不可变类型时,只能读取不能更新,如果使用类似count += 1就会隐式的把count变成局部变量,所以开始例子中使用series,我们后面的操作是append并且列表还是可变对象 不过python3引入了一个新的关键词nonlocal,通过它把变量标记为自由变量,这样我们把上面这个错误的例子简单更改: def make_averager(): count = 0 total = 0 def averager(new_value): nonlocal count,total count += 1 total += new_value return total / count return averager 到这里装饰器的前奏就说完了,下面就是装饰器,我个人觉得装饰器只是闭包的一种应用,闭包在很多情况下都是一种非常好的变成技巧。 以上是全部代码,只是善于分享,不足之处请包涵!爬虫基本的原理就是,获取源码,进而获取网页内容。一般来说,只要你给一个入口,通过分析,可以找到无限个其他相关的你需要的资源,进而进行爬取。 我也写了很多其他的非常简单的入门级的爬虫详细教程,关注后,点击我的头像,就可以查看到。 欢迎大家一起留言讨论和交流,谢谢! |
|