python核心高级学习总结8------动态性、__slots__、生成器、迭代器、装饰、闭包

来源:互联网 发布:数据预测模型有哪些 编辑:程序博客网 时间:2024/06/06 03:13

python的动态性

什么是动态性呢,简单地来说就是可以在运行时可以改变其结构,如:新的函数、对象、代码都可以被引进或者修改,除了Python外,还有Ruby、PHP、javascript等也是动态语言。下面是python中常用动态性

运行的过程中给对象绑定(添加)属性

>>> class Person(object):    def __init__(self, name = None, age = None):    self.name = name    self.age = age>>> P = Person("⼩明", "24")>>>
>>> P.sex = "male">>> P.sex'male'>>>
以上的例子很好理解,就是动态给实例绑定属性。

运行的过程给类绑定(添加)属性

>>> P1 = Person("⼩丽", "25")>>> P1.sex>>>> Person.sex = None #给类Person添加⼀个属性>>> P1 = Person("⼩丽", "25")>>> print(P1.sex) #如果P1这个实例对象中没有sex属性的话, 那么就会访问它的类属性None #可以看到没有出现异常>>>

运行的过程中给类绑定(添加)方法(类方法和静态方法)

运行的过程给对象绑定(添加)方法(类方法和静态方法)

import types#定义了⼀个类class Person(object):num = 0def __init__(self, name = None, age = None):self.name = nameself.age = agedef eat(self):print("eat food")#定义⼀个实例⽅法def run(self, speed):print("%s在移动, 速度是 %d km/h"%(self.name, speed))#定义⼀个类⽅法@classmethoddef testClass(cls):cls.num = 100#定义⼀个静态⽅法@staticmethoddef testStatic():print("---static method----")#创建⼀个实例对象P = Person("⽼王", 24)#调⽤在class中的⽅法P.eat()#给这个对象添加实例⽅法P.run = types.MethodType(run, P)#调⽤实例⽅法P.run(180)#给Person类绑定类⽅法Person.testClass = testClass#调⽤类⽅法print(Person.num)Person.testClass()print(Person.num)#给Person类绑定静态⽅法Person.testStatic = testStatic#调⽤静态⽅法Person.testStatic()
注意需要导入types,这是个类或者函数,统称其为模块。要记住调用方式types.MethodType(run,P)

运行的过程中删除属性或方法

两种⽅法:
1. del 对象.属性名
2. delattr(对象, "属性名")

__slots__

可以看到,相对于静态语言,动态语言没有那么严谨,所以玩的时候一定要小心其坑,如果想避免这种情况,请使用__slots__,为了达到限制的⽬的, Python允许在定义class的时候, 定义⼀个特殊的__slots__变量, 来限制该class实例能添加的属性:
>>> class Person(object):    __slots__ = ("name", "age")>>> P = Person()>>> P.name = "⽼王">>> P.age = 20>>> P.score = 100Traceback (most recent call last):File "<pyshell#3>", line 1, in <module>AttributeError: Person instance has no attribute 'score'>>>
注意:
  • 使⽤__slots__要注意, __slots__定义的属性仅对当前类实例起作⽤, 对继承的⼦类是不起作⽤的

生成器

1. 什么是⽣成器
通过列表⽣成式, 我们可以直接创建⼀个列表。 但是, 受到内存限制, 列表容量肯定是有限的。 ⽽且, 创建⼀个包含100万个元素的列表, 不仅占⽤很⼤的存储空间, 如果我们仅仅需要访问前⾯⼏个元素, 那后⾯绝⼤多数元素占⽤的空间都⽩⽩浪费了。 所以, 如果列表元素可以按照某种算法推算出来, 那我们是否可以在循环的过程中不断推算出后续的元素呢? 这样就不必创建完整的list, 从⽽节省⼤量的空间。 在Python中, 这种⼀边循环⼀边计算的机制, 称为⽣成器: generator。
2. 创建⽣成器⽅法1
要创建⼀个⽣成器, 有很多种⽅法。 第⼀种⽅法很简单, 只要把⼀个列表⽣成式的 [ ] 改成 ( )
In [15]: L = [ x*2 for x in range(5)]In [16]: LOut[16]: [0, 2, 4, 6, 8]In [17]: G = ( x*2 for x in range(5))In [18]: GOut[18]: <generator object <genexpr> at 0x7f626c132db0>In [19]:
创建 L 和 G 的区别仅在于最外层的 [ ] 和 ( ) , L 是⼀个列表, ⽽ G 是⼀个⽣成器。 我们可以直接打印出L的每⼀个元素, 但我们怎么打印出G的每⼀个元素呢? 如果要⼀个⼀个打印出来, 可以通过 next() 函数获得⽣成器的下⼀
个返回值:
In [19]: next(G)Out[19]: 0In [20]: next(G)Out[20]: 2In [21]: next(G)Out[21]: 4In [22]: next(G)Out[22]: 6In [23]: next(G)Out[23]: 8In [24]: next(G)------------------------------------------------------------------------StopIteration Traceback (most recent call las<ipython-input-24-380e167d6934> in <module>()----> 1 next(G)StopIteration:In [25]:In [26]: G = ( x*2 for x in range(5))In [27]: for x in G:....: print(x)
0 2 4 6 8 In [28]:
⽣成器保存的是算法, 每次调⽤ next(G) , 就计算出 G 的下⼀个元素的值,直到计算到最后⼀个元素, 没有更多的元素时, 抛出 StopIteration 的异常。当然, 这种不断调⽤ next() 实在是太变态了, 正确的⽅法是使⽤ for 循环,因为⽣成器也是可迭代对象。 所以, 我们创建了⼀个⽣成器后, 基本上永远不会调⽤ next() , ⽽是通过 for 循环来迭代它, 并且不需要关⼼StopIteration 异常。3. 创建⽣成器⽅法2generator⾮常强⼤。 如果推算的算法⽐较复杂, ⽤类似列表⽣成式的 for 循环⽆法实现的时候, 还可以⽤函数来实现。⽐如, 著名的斐波拉契数列( Fibonacci) , 除第⼀个和第⼆个数外, 任意⼀个数都可由前两个数相加得到:1, 1, 2, 3, 5, 8, 13, 21, 34, ...斐波拉契数列⽤列表⽣成式写不出来, 但是, ⽤函数把它打印出来却很容易:
In [28]: def fib(times):....: n = 0....: a,b = 0,1....: while n<times:....: print(b)....: a,b = b,a+b....: n+=1....: return 'done'....:In [29]: fib(5)1 1 2 3 5 Out[29]: 'done'
仔细观察, 可以看出, fib函数实际上是定义了斐波拉契数列的推算规则, 可以从第⼀个元素开始, 推算出后续任意的元素, 这种逻辑其实⾮常类似generator。也就是说, 上⾯的函数和generator仅⼀步之遥。 要把fib函数变成generator,只需要把print(b)改为yield b就可以了:
In [30]: def fib(times):....: n = 0....: a,b = 0,1....: while n<times:....: yield b....: a,b = b,a+b....: n+=1....: return 'done'....:In [31]: F = fib(5)In [32]: next(F)Out[32]: 1In [33]: next(F)Out[33]: 1
在上⾯fib 的例⼦, 我们在循环过程中不断调⽤ yield , 就会不断中断。 当然要给循环设置⼀个条件来退出循环, 不然就会产⽣⼀个⽆限数列出来。 同样的, 把函数改成generator后, 我们基本上从来不会⽤ next() 来获取下⼀个返
回值, ⽽是直接使⽤ for 循环来迭代。但是⽤for循环调⽤generator时, 发现拿不到generator的return语句的返回
值。 如果想要拿到返回值, 必须捕获StopIteration错误, 返回值包含在StopIteration的value中。

In [39]: g = fib(5)In [40]: while True:....: try:....: x = next(g)....: print("value:%d"%x)....: except StopIteration as e:....: print("⽣成器返回值:%s"%e.value)....: break....:value:1value:1value:2value:3value:5⽣成器返回值:doneIn [41]:
4. send
例⼦: 执⾏到yield时, gen函数作⽤暂时保存, 返回i的值;temp接收下次c.send("python"), send发送过来的值, c.next()等价c.send(None)
In [10]: def gen():....: i = 0....: while i<5:....: temp = yield i....: print(temp)....: i+=1....:
#使用next()方法In [11]: f = gen()In [12]: next(f)Out[12]: 0In [13]: next(f)NoneOut[13]: 1In [14]: next(f)NoneOut[14]: 2In [15]: next(f)NoneOut[15]: 3In [16]: next(f)NoneOut[16]: 4In [17]: next(f)None------------------------------------------------------------------------StopIteration Traceback (most recent call las<ipython-input-17-468f0afdf1b9> in <module>()----> 1 next(f)StopIteration:
使用send()
In [43]: f = gen()In [44]: f.__next__()Out[44]: 0In [45]: f.send('haha')hahaOut[45]: 1In [46]: f.__next__()NoneOut[46]: 2In [47]: f.send('haha')hahaOut[47]: 3In [48]:
最后总结:⽣成器是这样⼀个函数, 它记住上⼀次返回时在函数体中的位置。 对⽣成器函数的第⼆次( 或第 n 次) 调⽤跳转⾄该函数中间, ⽽上次调⽤的所有局部变量都保持不变。⽣成器不仅“记住”了它数据状态; ⽣成器还“记住”了它在流控制构造( 在命令式编程中, 这种构造不只是数据值) 中的位置。
⽣成器的特点:
1. 节约内存
2. 迭代到下⼀次的调⽤时, 所使⽤的参数都是第⼀次所保留下的, 即是说, 在整个所有函数调⽤的参数都是第⼀次所调⽤时保留的, ⽽不是新创建的

迭代器

迭代器跟其他语言类似,从一个可迭代的对象中逐步遍历其值,而且只会前进,不会后退。
1.可迭代对象Iterable
也即可以用for循环来遍历的数据类型
⼀类是集合数据类型, 如 list 、 tuple 、 dict 、 set 、 str 等;
⼀类是 generator , 包括⽣成器和带 yield 的generator function。
可以使⽤ isinstance() 判断⼀个对象是否是 Iterable 对象:
In [50]: from collections import IterableIn [51]: isinstance([], Iterable)Out[51]: TrueIn [52]: isinstance({}, Iterable)Out[52]: TrueIn [53]: isinstance('abc', Iterable)Out[53]: TrueIn [54]: isinstance((x for x in range(10)), Iterable)Out[54]: TrueIn [50]: from collections import IterableIn [51]: isinstance([], Iterable)Out[51]: TrueIn [52]: isinstance({}, Iterable)Out[52]: TrueIn [53]: isinstance('abc', Iterable)Out[53]: TrueIn [54]: isinstance((x for x in range(10)), Iterable)Out[54]: True
⽽⽣成器不但可以作⽤于 for 循环, 还可以被 next() 函数不断调⽤并返回下⼀个值, 直到最后抛出 StopIteration 错误表示⽆法继续返回下⼀个值了。
2.迭代器Iterator
In [62]: isinstance(iter([]), Iterator)Out[62]: TrueIn [63]: isinstance(iter('abc'), Iterator)Out[63]: True
可以使⽤ isinstance() 判断⼀个对象是否是 Iterator 对象:
In [56]: from collections import IteratorIn [57]: isinstance((x for x in range(10)), Iterator)Out[57]: TrueIn [58]: isinstance([], Iterator)Out[58]: FalseIn [59]: isinstance({}, Iterator)Out[59]: FalseIn [60]: isinstance('abc', Iterator)Out[60]: FalseIn [61]: isinstance(100, Iterator)Out[61]: False
4.Iter函数
⽣成器都是 Iterator 对象, 但 list 、 dict 、 str 虽然是 Iterable , 却不是Iterator 。把 list 、 dict 、 str 等 Iterable 变成 Iterator 可以使⽤ iter() 函数:
In [62]: isinstance(iter([]), Iterator)Out[62]: TrueIn [63]: isinstance(iter('abc'), Iterator)Out[63]: True
总结:
  • 凡是可作⽤于 for 循环的对象都是 Iterable 类型;
  • 凡是可作⽤于 next() 函数的对象都是 Iterator 类型集合数据类型如 list 、 dict 、 str 等是 Iterable 但不是 Iterator, 不过可以通过 iter() 函数获得⼀个 Iterator 对象。

闭包

什么是闭包?闭包有什么用处
#定义⼀个函数def test(number):#在函数内部再定义⼀个函数, 并且这个函数⽤到了外边函数的变量, 那么将这个函数以及⽤到def test_in(number_in):print("in test_in 函数, number_in is %d"%number_in)return number+number_in#其实这⾥返回的就是闭包的结果return test_in#给test函数赋值, 这个20就是给参数numberret = test(20)#注意这⾥的100其实给参数number_inprint(ret(100))#注意这⾥的200其实给参数number_inprint(ret(200))
运行结果:
in test_in 函数, number_in is 100120in test_in 函数, number_in is 200220
内部函数对外部函数作⽤域⾥变量的引⽤( ⾮全局变量) , 则称内部函数为闭包。
# closure.pydef counter(start=0):    count=[start]    def incr():    count[0] += 1
    return count[0]    return incr
启动python解释器
>>>import closeure>>>c1=closeure.counter(5)>>>print(c1())6 >>>print(c1())7 >>>c2=closeure.counter(100)>>>print(c2())101>>>print(c2())102
nonlocal访问外部函数的局部变量(python3)
def counter(start=0):    def incr():        nonlocal start        start += 1        return start    return incrc1 = counter(5)print(c1())print(c1())c2 = counter(50)print(c2())print(c2())print(c1())print(c1())print(c2())print(c2())
4,下面是闭包的一个实际例子
def line_conf(a, b):    def line(x):       return a*x + b    return lineline1 = line_conf(1, 1)line2 = line_conf(4, 5)print(line1(5))print(line2(5))
这个例⼦中, 函数line与变量a,b构成闭包。 在创建闭包的时候, 我们通过liine_conf的参数a,b说明了这两个变量的取值, 这样, 我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。 我们只需要变换参数a,b, 就可以获得不同的直线表达函数。 由此, 我们可以看到, 闭包也具有提⾼代码可复⽤性的作⽤。
如果没有闭包, 我们需要每次创建直线函数的时候同时说明a,b,x。 这样, 我们就需要更多的参数传递, 也减少了代码的可移植性。
闭包思考:
1.闭包似优化了变量, 原来需要类对象完成的⼯作, 闭包也可以完成
2.由于闭包引⽤了外部函数的局部变量, 则外部函数的局部变量没有及时释放, 消耗内存

装饰器

通过下面一个小例子来理解装饰器,具体的关于闭包跟装饰器的内容有篇博客写的更详细,可以看看说说python中的闭包与装饰器。
#定义函数: 完成包裹数据def makeBold(fn):def wrapped():return "<b>" + fn() + "</b>"return wrapped#定义函数: 完成包裹数据def makeItalic(fn):def wrapped():return "<i>" + fn() + "</i>"return wrapped@makeBolddef test1():return "hello world-1"@makeItalicdef test2():return "hello world-2"@makeBold@makeItalicdef test3():return "hello world-3"print(test1()))print(test2()))print(test3()))
运行结果:
<b>hello world-1</b><i>hello world-2</i><b><i>hello world-3</i></b>
是不是觉得很简单,是不是觉得SO EASY!
装饰器一般用于下面几个场景
1. 引⼊⽇志
2. 函数执⾏时间统计
3. 执⾏函数前预备处理
4. 执⾏函数后清理功能
5. 权限校验等场景
6. 缓存
阅读全文
2 0
原创粉丝点击