python的动态性
什么是动态性呢,简单地来说就是可以在运行时可以改变其结构,如:新的函数、对象、代码都可以被引进或者修改,除了Python外,还有Ruby、PHP、javascript等也是动态语言。下面是python中常用动态性:
运行的过程中给对象绑定(添加)属性
>>>> class Person(object): |
以上的例子很好理解,就是动态给实例绑定属性。
运行的过程给类绑定(添加)属性
>>>> P1 = Person("小丽", "25") |
运行的过程中给类和对象绑定方法(类方法和静态方法)
import types |
注意: 需要导入types,这是个类或者函数,统称其为模块。要记住调用方式types.MethodType(run,P)
运行的过程中删除属性或方法
两种方法:
- del 对象.属性名
- delattr(对象, “属性名”)
slots
可以看到,相对于静态语言,动态语言没有那么严谨,所以玩的时候一定要小心其坑,如果想避免这种情况,请使用__slots_
_,为了达到限制的目的, Python允许在定义class的时候, 定义一个特殊的 _slots_
变量, 来限制该class实例能添加的属性:
class Person(object): |
注意:
- 使
_slots_
要注意,_slots_
定义的属性仅对当前类实例起作用, 对继承的子类是不起作用的。
生成器
- 什么是生成器
通过列表生成式, 我们可以直接创建生个列表。 但是, 受到内存限制, 列表容量肯定是有限的。 而且, 创建一个包含100万个元素的列表, 不仅占用很多的存储空间, 如果我们仅仅需要访问前一百个元素, 那后面绝大多数元素占用的空间都白白浪费了。 所以, 如果列表元素可以按照某种算法推算出来, 那我们是否可以在循环的过程中不断推算出后续的元素呢? 这样就不必创建完整的list, 从而节省空间。 在Python中, 这种一边循环一边计算的机制, 称为生成器: generator。
- 创建生成器方法1
要创建一个生成器, 有很多种方法。 第一种方法很简单, 只要把一个列表生成式的 [ ] 改成 ( )
In [15]: L = [ x*2 for x in range(5)] |
创建 L 和 G 的区别仅在于最外层的 [ ] 和 ( ) , L 是一个列表, 而 G 是一个生成器。 我们可以直接打印出L的每一个元素, 但我们怎么打印出G的每一个元素呢? 如果要一个一个打印出来, 可以通过 next() 函数获得生成器的下一个返回值:
In [19]: next(G) |
生成器保存的是算法, 每次调用 next(G) , 就计算出 G 的下一个元素的值,直到计算到最后一个元素, 没有更多的元素时, 抛出 StopIteration 的异常。当然, 这种不断调用 next() 实在是太变态了, 正确的做法是使用for 循环,因为生成器也是可迭代对象。 所以, 我们创建了一个生成器后, 基本上永远不会调用 next() , 而是通过 for 循环来迭代它, 并且不需要关心StopIteration 异常。
创建生成器方法2
如果推算的算法比较复杂,像类似列表生成式的 for 循环方法实现的时候, 还可以用函数来实现。比如, 著名的斐波拉契数列( Fibonacci) , 除第一个和第二个数外, 任意一个数都可由前两个数相加得到:1, 1, 2, 3, 5, 8, 13, 21, 34, …斐波拉契数列列表生成式写不出来, 但是, 用函数把它打印出来却很容易:
In [28]: def fib(times): |
仔细观察, 可以看出, fib函数实际上是定义了斐波拉契数列的推算规则, 可以从第一个元素开始, 推算出后续任意的元素, 这种逻辑其实非常类似generator。也就是说, 上⾯的函数和generator仅一步之遥。 要把fib函数变成generator,只需要把print(b)改为yield b就可以了:
In [30]: def fib(times): |
在上一fib 的例子, 我们在循环过程中不断调用 yield , 就会不断中断。 当然要给循环设置一个条件来退出循环, 不然就会产生个无限数列出来。 同样的, 把函数改成generator后, 我们基本上从来不会⽤从next() 来获取下一个返回值, 而是直接使用for 循环来迭代。但是用for循环调用generator时, 发现拿不到generator的return语句的返回值。 如果想要拿到返回值, 必须捕获StopIteration错误, 返回值包含在StopIteration的value中。
In [39]: g = fib(5) |
- send
例子: 执行到yield时, gen函数作用暂时保存, 返回i的值; temp接收下次c.send("python")
, send发送过来的值, c.next()
等价c.send(None)
。
In [10]: def gen(): |
使用send()
In [43]: f = gen() |
最后总结:生成器是这样一个函数, 它记住上一次返回时在函数体中的位置。 对生成器函数的第意次( 或第 n 次) 调⽤跳转到该函数中间, 而上次调用的所有局部变量都保持不变。生成器不仅“记住”了它数据状态; 生成器还“记住”了它在流控制构造( 在命令式编程中, 这种构造不只是数据值) 中的位置。
生成器的特点:
- 节约内存
- 迭代到下一次的调用时, 所使用的参数都是第一次所保留下的, 即是说, 在整个所有函数调用的参数都是第一次所调用时保留的, 而不是新创建的。
迭代器
迭代器跟其他语言类似,从一个可迭代的对象中逐步遍历其值,而且只会前进,不会后退。
1.可迭代对象Iterable
也即可以用for循环来遍历的数据类型
- 一类是集合数据类型, 如 list 、 tuple 、 dict 、 set 、 str 等;
- 一类是 generator , 包括⽣成器和带 yield 的generator function。
可以使用isinstance()
判断一个对象是否是 Iterable 对象:
In [50]: from collections import Iterable |
生成器不但可以作用于 for 循环, 还可以被 next() 函数不断调⽤并返回下一个值, 直到最后抛出 StopIteration 错误表示方法继续返回下一个值了。
2.迭代器Iterator
In [62]: isinstance(iter([]), Iterator) |
可以使用isinstance()
判断一个对象是否是 Iterator 对象:
n [56]: from collections import Iterator |
3.Iter函数
生成器都是 Iterator 对象, 但 list 、 dict 、 str 虽然是 Iterable , 却不是Iterator 。把 list 、 dict 、 str 等 Iterable 变成 Iterator 可以使用iter()
函数:
In [62]: isinstance(iter([]), Iterator) |
总结:
- 凡是可作用于 for 循环的对象都是 Iterable 类型;
- 凡是可作用于 next() 函数的对象都是 Iterator 类型集合数据类型如 list 、 dict 、 str 等是 Iterable 但不是 Iterator, 不过可以通过
iter()
函数获得一个 Iterator 对象。
闭包
什么是闭包?闭包有什么用处
#定义一个函数 |
运行结果:
in test_in 函数, number_in is 100 |
内部函数对外部函数作用域的变量的引用(全局变量),则称内部函数为闭包。
# closure.py |
启动python解释器
>>>import closeure |
nonlocal访问外部函数的局部变量(python3)
def counter(start=0): |
下面是闭包的一个实际例子
def line_conf(a, b): |
这个例子中, 函数line与变量a,b构成闭包。 在创建闭包的时候, 我们通过line_conf
的参数a,b说明了这两个变量的取值, 这样, 我们就确定了函数的最终形式(y = x + 1
和y = 4x + 5)
。 我们只需要变换参数a,b, 就可以获得不同的直线表达函数。 由此, 我们可以看到, 闭包也具有提高代码可复用性的作用。如果没有闭包, 我们需要每次创建直线函数的时候同时说明a,b,x
。 这样, 我们就需要更多的参数传递, 也减少了代码的可移植性。
闭包思考:
1.闭包似优化了变量, 原来需要类对象完成的工作, 闭包也可以完成。
2.由于闭包引用了外部函数的局部变量, 则外部函数的局部变量没有及时释放, 消耗内存。
装饰器
通过下面一个小例子来理解装饰器,具体的关于闭包跟装饰器的内容有篇博客写的更详细,可以看看说说python中的闭包与装饰器。
#定义函数: 完成包裹数据 |
运行结果:
<b>hello world-1</b> |
是不是觉得很简单,是不是觉得SO EASY!
装饰器一般用于下面几个场景
- 引用日志
- 函数执行时间统计
- 执行函数前预备处理
- 执行函数后清理功能
- 权限校验等场景
- 缓存