python核心高级学习总结8------动态性、__slots__、生成器、迭代器、装饰、闭包
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 = 0
def __init__(self, name = None, age = None):
self.name = name
self.age = age
def eat(self):
print("eat food")
#定义⼀个实例⽅法
def run(self, speed):
print("%s在移动, 速度是 %d km/h"%(self.name, speed))
#定义⼀个类⽅法
@classmethod
def testClass(cls):
cls.num = 100
#定义⼀个静态⽅法
@staticmethod
def 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()
运行的过程中删除属性或方法
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 = 100
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
AttributeError: Person instance has no attribute 'score'
>>>
注意:
- 使⽤__slots__要注意, __slots__定义的属性仅对当前类实例起作⽤, 对继承的⼦类是不起作⽤的
生成器
通过列表⽣成式, 我们可以直接创建⼀个列表。 但是, 受到内存限制, 列表容量肯定是有限的。 ⽽且, 创建⼀个包含100万个元素的列表, 不仅占⽤很⼤的存储空间, 如果我们仅仅需要访问前⾯⼏个元素, 那后⾯绝⼤多数元素占⽤的空间都⽩⽩浪费了。 所以, 如果列表元素可以按照某种算法推算出来, 那我们是否可以在循环的过程中不断推算出后续的元素呢? 这样就不必创建完整的list, 从⽽节省⼤量的空间。 在Python中, 这种⼀边循环⼀边计算的机制, 称为⽣成器: generator。
2. 创建⽣成器⽅法1
要创建⼀个⽣成器, 有很多种⽅法。 第⼀种⽅法很简单, 只要把⼀个列表⽣成式的 [ ] 改成 ( )
In [15]: L = [ x*2 for x in range(5)]
In [16]: L
Out[16]: [0, 2, 4, 6, 8]
In [17]: G = ( x*2 for x in range(5))
In [18]: G
Out[18]: <generator object <genexpr> at 0x7f626c132db0>
In [19]:
创建 L 和 G 的区别仅在于最外层的 [ ] 和 ( ) , L 是⼀个列表, ⽽ G 是⼀个⽣成器。 我们可以直接打印出L的每⼀个元素, 但我们怎么打印出G的每⼀个元素呢? 如果要⼀个⼀个打印出来, 可以通过 next() 函数获得⽣成器的下⼀
个返回值:
In [19]: next(G)
Out[19]: 0
In [20]: next(G)
Out[20]: 2
In [21]: next(G)
Out[21]: 4
In [22]: next(G)
Out[22]: 6
In [23]: next(G)
Out[23]: 8
In [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 O
ut[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]: 1
In [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:1
value:1
value:2
value:3
value:5
⽣成器返回值:done
In [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]: 0
In [13]: next(f)
None
Out[13]: 1
In [14]: next(f)
None
Out[14]: 2
In [15]: next(f)
None
Out[15]: 3
In [16]: next(f)
None
Out[16]: 4
In [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]: 0
In [45]: f.send('haha')
haha
Out[45]: 1
In [46]: f.__next__()
None
Out[46]: 2
In [47]: f.send('haha')
haha
Out[47]: 3
In [48]:
最后总结:⽣成器是这样⼀个函数, 它记住上⼀次返回时在函数体中的位置。 对⽣成器函数的第⼆次( 或第 n 次) 调⽤跳转⾄该函数中间, ⽽上次调⽤的所有局部变量都保持不变。⽣成器不仅“记住”了它数据状态; ⽣成器还“记住”了它在流控制构造( 在命令式编程中, 这种构造不只是数据值) 中的位置。
⽣成器的特点:
1. 节约内存
2. 迭代到下⼀次的调⽤时, 所使⽤的参数都是第⼀次所保留下的, 即是说, 在整个所有函数调⽤的参数都是第⼀次所调⽤时保留的, ⽽不是新创建的
迭代器
也即可以用for循环来遍历的数据类型
⼀类是 generator , 包括⽣成器和带 yield 的generator function。
可以使⽤ isinstance() 判断⼀个对象是否是 Iterable 对象:
In [50]: from collections import Iterable
In [51]: isinstance([], Iterable)
Out[51]: True
In [52]: isinstance({}, Iterable)
Out[52]: True
In [53]: isinstance('abc', Iterable)
Out[53]: True
In [54]: isinstance((x for x in range(10)), Iterable)
Out[54]: True
In [50]: from collections import Iterable
In [51]: isinstance([], Iterable)
Out[51]: True
In [52]: isinstance({}, Iterable)
Out[52]: True
In [53]: isinstance('abc', Iterable)
Out[53]: True
In [54]: isinstance((x for x in range(10)), Iterable)
Out[54]: True
⽽⽣成器不但可以作⽤于 for 循环, 还可以被 next() 函数不断调⽤并返回下⼀个值, 直到最后抛出 StopIteration 错误表示⽆法继续返回下⼀个值了。
In [62]: isinstance(iter([]), Iterator)
Out[62]: True
In [63]: isinstance(iter('abc'), Iterator)
Out[63]: True
In [56]: from collections import Iterator
In [57]: isinstance((x for x in range(10)), Iterator)
Out[57]: True
In [58]: isinstance([], Iterator)
Out[58]: False
In [59]: isinstance({}, Iterator)
Out[59]: False
In [60]: isinstance('abc', Iterator)
Out[60]: False
In [61]: isinstance(100, Iterator)
Out[61]: False
4.Iter函数
In [62]: isinstance(iter([]), Iterator)
Out[62]: True
In [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就是给参数number
ret = test(20)
#注意这⾥的100其实给参数number_in
print(ret(100))
#注意这⾥的200其实给参数number_in
print(ret(200))
运行结果:
in test_in 函数, number_in is 100
120
in test_in 函数, number_in is 200
220
内部函数对外部函数作⽤域⾥变量的引⽤( ⾮全局变量) , 则称内部函数为闭包。
# closure.py
def 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 incr
c1 = 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 line
line1 = 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.由于闭包引⽤了外部函数的局部变量, 则外部函数的局部变量没有及时释放, 消耗内存
装饰器
#定义函数: 完成包裹数据
def makeBold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
#定义函数: 完成包裹数据
def makeItalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
@makeBold
def test1():
return "hello world-1"
@makeItalic
def test2():
return "hello world-2"
@makeBold
@makeItalic
def 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!
2. 函数执⾏时间统计
3. 执⾏函数前预备处理
4. 执⾏函数后清理功能
5. 权限校验等场景
6. 缓存
python核心高级学习总结8------动态性、__slots__、生成器、迭代器、装饰、闭包的更多相关文章
- python核心高级学习总结5--------python实现线程
在代码实现上,线程的实现与进程的实现很类似,创建对象的格式都差不多,然后执行的时候都是用到start()方法,与进程的区别是进程是资源分配和调度的基本单位,而线程是CPU调度和分派的基本单位.其中多线 ...
- python核心高级学习总结7---------正则表达式
正则表达式在爬虫项目中应用很广泛,主要方面就是在字符串处理方面,经常会涉及到字符串格式的校验,用起来经常要查看文档才能完成,所以抽了个时间将正则的内容复习了一下. Start re---导入re模块使 ...
- python核心高级学习总结6------面向对象进阶之元类
元类引入 在多数语言中,类就是一组用来描述如何生成对象的代码段,在python中同样如此,但是在python中把类也称为类对象,是的,你没听错,在这里你只要使用class关键字定义了类,其解释器在执行 ...
- python核心高级学习总结3-------python实现进程的三种方式及其区别
python实现进程的三种方式及其区别 在python中有三种方式用于实现进程 多进程中, 每个进程中所有数据( 包括全局变量) 都各有拥有⼀份, 互不影响 1.fork()方法 ret = os.f ...
- python核心高级学习总结1---------*args和**kwargs
*args 和 ** kwargs 的用法 首先,这两者在用法上都是用来补充python中对不定参数的接受. 比如下面的列子 def wrappedfunc(*args, **kwargs): pri ...
- python核心高级学习总结4-------python实现进程通信
Queue的使用 Queue在数据结构中也接触过,在操作系统里面叫消息队列. 使用示例 # coding=utf-8 from multiprocessing import Queue q = Que ...
- python核心高级学习总结2----------pdb的调试
PDB调试 def getAverage(a,b): result =a+b print("result=%d"%result) return result a=100 b=200 ...
- 零基础的学习者应该怎么开始学习呢?Python核心知识学习思维分享
近几年,Python一路高歌猛进,成为最受欢迎的编程语言之一,受到无数编程工作者的青睐. 据悉,Python已经入驻部分小学生教材,可以预见学习Python将成为一项提高自身职业竞争力的必修课.那么零 ...
- Python核心编程--学习笔记--8--条件与循环
本章讲述if.while.for以及与他们搭配的else.elif.break.continue.pass等语句. 1 if语句 语法:三部分——关键字if.条件表达式.代码块.(记住冒号) if c ...
随机推荐
- 作为servlet容器的hi-nginx-java
hi-nginx-java是一个独立于java官方的servlet规范,它有能力把NGINX直接编成servlet容器服务器.换言之,无需安装tomcat等容器服务器,也无需使用nginx的反向代理功 ...
- 第一行代码中RecyclerView添加依赖库问题
现在更新到 implementation 'com.android.support:recyclerview-v7:29.2.1' 记得点Sync Now来进行同步.
- 从比心APP源码的成功,分析陪玩系统源码应该如何开发
提起游戏陪玩系统,相信大家都不陌生.作为一名骨灰级的手游玩家,小编对于陪玩系统源码也有些了解.在互联网络发展愈发迅速的今天,游戏产业在一中领域中脱颖而出,据统计,手机游戏用户已经达到5.29亿,较20 ...
- js 图片放大镜功能
原理:放置两张相同的图片,一张作为主图片(图片1),另一张作为用来裁剪并放大的图片(图片2) 鼠标移动时,计算鼠标在图片1的位置(距离图片1左上角的x,y距离),以此决定在图片2开始 ...
- .net 实现之短信验证码
接口类型:互亿无线触发短信接口,支持发送验证码短信.订单通知短信等. 账户注册:请通过该地址开通账户http://sms.ihuyi.com/register.html 只能测试用: 实现注册页面 & ...
- 【Python】如何结束退出 py 脚本
需求 当你运行脚本,在判断条件满足时,就退出脚本,结束本次执行. 方法 使用 sys.exit(),直接退出程序,但是会引发一个 SystemExit 异常: 该方法包含一个 status 参数 sy ...
- malloc/free与new/delete的区别(转)
相同点:都可用于申请动态内存和释放内存 不同点:(1)操作对象有所不同.malloc与free是C++/C 语言的标准库函数,new/delete 是C++的运算符.对于非内部数据类的对象而言,光用m ...
- 【Java从入门到精通】day08-包机制-JavaDoc生成文档
1.包机制 为了更好地组织类,Java提供了包机制,用于区别类名的命名空间. 包语句的语法格式为: package pkg1[.pkg2[.pkg3...]]; 一般利用公司域名倒置作为包名(如www ...
- WIN10—更改电脑桌面路径
电脑默认的桌面路径一般都在C盘,而我们又特别喜欢把文件都放在桌面,因为桌面既方便又好找.可时间久了,桌面文件会越来越多,C盘空间会越来越小,会拖慢系统速度.怎么把系统桌面路径设置在非C盘呢?本期教程将 ...
- Unity CommandBuffer物体轮廓
1.command buffer具有很高的灵活性,它的作用是预定义一些渲染指令,然后在我们想要执行的时候去执行这些指令(见图1),绿点表示可以在"Forward Rendering Path ...