简学Python第四章__装饰器、迭代器、列表生成式
Python第四章__装饰器、迭代器
欢迎加入Linux_Python学习群
群号:478616847
目录:
列表生成式
生成器
迭代器
单层装饰器(无参)
多层装饰器(有参)
冒泡算法
代码开发规范
一、列表生成式(列表推导式)
列表生成式List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。
首先来上个需求,我有一个列表 [1,2,3,4,5,6,7,8,9,10],现在有这么个需求,要里面的元素自乘,想想看要怎么实现
版本一,通过for循环,重新赋值(占内存空间)
a = [1,2,3,4,5,6,7,8,9,10]
b = []
for i in a:
b.append(i*i)
a = b
print(a)
版本一
版本二,通过for循环修改原值(代码太多)
a = [1,2,3,4,5,6,7,8,9,10]
for indexs,i in enumerate(a):
a[indexs] *= i
print(a)
版本二
版本三,借助map和lambda(代码也不少)
a = [1,2,3,4,5,6,7,8,9,10]
a = map(lambda x:x*x,a)
print(list(a))
版本三
列表生成式(Python的高级特性),一句话即可实现上面的功能,在列表生成式中我们可以加if判断,也可以加多个for循环,多个for循环,我们就把它
想成for循环嵌套,并且实际结果也是for循环嵌套的结果
a = [i*i for i in range(1,11)]
print(a) #加if判断
a = [i*i for i in range(1,11) if i%2 == 1 ]
print(a) #多层for循环
a = [a+b+c for a in "abc" for b in "ABC" for c in ""]
print(a) #多层for循环解析
s = []
for a in "abc" :
for b in "ABC":
for c in "":
s.append(a+b+c)
print(s)
列表生成式
二、生成器
生成器也叫简单生成器,它是可以简单有效的创建出迭代器的工具,并且通过yield语句,当每次对有yield语句函数使用next()的时候
生成器会从yield停止的位置继续开始
生成器的特性:
1、生成器是用普通的函数语法定义的迭代器
2、通过next()语句调用,生成器函数将从yield语句处继续执行
3、节省空间,生成器只是指定的计算方式,在不调动的时候不会生成所有的值
4、通过生成器的这种可以再次从yield语句处再次执行的特性,我们可以完成协同程序(下面例子说明)
协同程序:协同程序是可以运行的独立函数调用,可以暂停或者挂起,并从程序离开的地方继续或者重新开始。
第一个简单生成器
下面的代码主要是定义一个函数,这个函数接收一个数字的参数,然后通过list(range(num))生成一个列表。并for循环这个列表
每次循环都触发 yield i ,当第一次循环触发yield i 则程序会停止到这里并且产生一个值,等待下一步激活,那么通过__next__()和next()
就去到yield 返回的值,并且从停止的位置继续执行,知道再碰到yield,或者执行完成。
def Out_num(num):
for i in list(range(num)):
yield i Builder = Out_num(5) #返回的不是一个数字而是一个迭代器
print(Builder) #取值方式
print("我是通过__next__()取值:",Builder.__next__())
print("我是通过next()取值:",next(Builder)) #for循环取值
print("我是通过for循环取值↓")
for i in Builder:
print(i)
生成器
要注意的一点是通过__next__()和next()取值时当生成器中没有值而你取值则程序会报错,通过for循环取值时不存在这个问题的!
>>> def Out_num(num):
... for i in list(range(num)):
... yield i
...
>>> Builder = Out_num(3) >>> Builder.__next__()
0
>>> Builder.__next__()
1
>>> Builder.__next__()
2
>>> Builder.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
生成器报错
协同程序与send()
我们知道了协同程序就是程序可以暂停或者挂起,并且可以从程序离开的地方继续或者重新开始
下面的例子是个吃包子与做包子的模拟程序,通过yield让consumer函数暂停,并执行做包子的操作,做完包子后通过send(),给yield赋值,
并且继续执行consumer函数中的代码,而且我们可以通过send()传进来的值判断做什么操作,就象例子中“梅菜肉馅的包子”一样,隔壁老王不吃而隔壁老李爱吃。
import time
def consumer(name):
print("%s 准备吃包子啦!" %name)
while True:
baozi = yield
if baozi == "牛肉" or baozi == "酸菜":
print("%s馅的包子来了,%s爱吃,被[%s]吃了!" %(baozi,name,name))
elif baozi == "梅菜肉" and name == "隔壁老李":
print("%s馅的包子来了,%s爱吃,被[%s]吃了!" %(baozi,name,name))
else:
print("%s馅的包子不好吃,%s不吃!" %(baozi,name)) def producer():
c = consumer('隔壁老王')
c2 = consumer('隔壁老李')
c.__next__()
c2.__next__()
print("来顾客了,大王开始做包子了!")
species = ["牛肉","素三鲜","酸菜","梅菜肉","韭菜"]
for i in species:
time.sleep(1)
print("---------------------分割线-------------------------")
print("做了%s馅的包子"%i)
c.send(i)
c2.send(i)
producer()
协同程序(只吃好吃的包子)
生成器表达式
在上面学习了列表生成式,和生成器,那么生成器表达式它和列表生成式的用法基本一致,其工作方式是每次处理一个对象,而不是一口气处理和构造整个数据结构,
也就是返回的是一个可迭代的对象,这样做的潜在优点是可以节省大量的内存,
语法:(expr for iter_var in iterable) 或 (expr for iter_var in iterable if cond_expr)
#!/usr/bin/env python
# -*- coding:utf-8 -*- #生成器表达式
c = (x for x in "ABCDEFG")
print(c)
#列表生成式
a = [i*i for i in range(1,11) if i%2 == 1 ]
print(a) lists = ["user","pass","age","gender"]
s = {i:"" for i in lists }
print(s)
生成器表达式
三、迭代器
什么是迭代器?一种是可以直接作用于for
循环的数据类型,另一种就是有一个 next() 方法的对象, 而不是通过索引来计数,也就说生成器生成的就是迭代器,
迭代器也有一些限制. 例如你不能向后移动,不能回到开始, 也不能复制一个迭代器,可以使用isinstance()
判断一个对象是否是可迭代:
from collections import Iterable
print(isinstance([],Iterable))
print(isinstance({},Iterable))
print(isinstance((),Iterable))
print(isinstance("abcd",Iterable))
#数字无法迭代
print(isinstance(100,Iterable))
判断对象是否可迭代
生成器都是迭代器,但是虽然 list dict str可以迭代,但是它们不是迭代器,通过iter()函数可以把可迭代的对象变成迭代器
from collections import Iterator
print(isinstance([],Iterator))
print(isinstance({},Iterator))
print(isinstance((),Iterator))
print(isinstance("abcd",Iterator)) print(isinstance((x for x in range(10)),Iterator))
判断是否是迭代器
你可能会问,为什么list、dict、str等数据类型不是Iterator(迭代器)?
这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。
可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性
的,只有在需要返回下一个数据时它才会计算。Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
四、单层装饰器(无参)
装饰器是函数式编程的重中之重!学好装饰器不仅能让你的代码看上去,B格更高,也能体现出技术含量,更能实现开发封闭原则,装饰器的背后主要动机源自
python 面向对象编程。所以装饰器是在函数调用之上的修饰。也就是说装饰器的作用是在不动函数源码的前提上给函数加功能,抽象的理解就是把函数装饰起
来,是函数执行前与执行后都能做不同的操作!
上故事有一家公司,准备面试新人,这个部门的老大亲自当面试官,并且呢,给面试者出了这么一道题
我们有这么一段代码,这段代码,这段代码假设是N个业务部门的业务的函数,这段代码的意思就是当我们调用上面的函数的时候,传入值给arg,
当arg的值等于f1或者f2那么对应的函数就返回ok
#!/usr/bin/env python
# -*- coding: utf-8 -*- def f1(arg):
print('我是F1业务')
if arg == 'f1':
return 'ok' def f2(arg):
print('我是F2业务')
if arg == 'f2':
return 'ok'
业务代码
那么公司有N个业务部门,1个基础平台部门,基础平台负责提供底层的功能,如:数据库操作、redis调用、监控API等功能。业务部门使用基础功能时,
只需调用基础平台提供的功能即可。那么我们业务部门调用功能的时候只需要:
f1(值)
f2(值)
然而呢我发现了一个问题就是业务部调用基础平台的功能的时候没有验证这样不好,所以请各位面试者把验证功能加上,并且业务部门在调用功能的方式不能变
应聘者LowA:
他是这么做的,他说跟各个做基础功能的人协调,要求在自己业务的代码上加入验证模块,那么这样呢整个的基础平台就不需要更改,结果,老大直接请他走了
应聘者LowB:
这个LowB看到LowA直接被赶出去了,心里烦了低估,同样的题LowB把自己每个基础代码函数里面都加上了验证代码,老大一看,随便问了几句,也让他回去等消息了
#!/usr/bin/env python
# -*- coding: utf-8 -*- def f1(arg):
#验证代码
#验证代码
print('我是F1业务')
if arg == 'f1':
return 'ok' def f2(arg):
#验证代码
#验证代码
print('我是F2业务')
if arg == 'f2':
return 'ok'
LowB
应聘者LowC:
他看到了这道题,他把验证代码单独写成一个函数,然后在基础函数中调用这个验证函数,老大看见了LowC的实现方式,嘴角露出了一丝微笑,并且与LowC聊了个天
#!/usr/bin/env python
# -*- coding: utf-8 -*- #验证函数
def verify():
# 验证1
# 验证2
# 验证3
pass def f1(arg):
verify()
print('我是F1业务')
if arg == 'f1':
return 'ok' def f2(arg):
verify()
print('我是F2业务')
if arg == 'f2':
return 'ok'
LowC
老大说:
写代码要遵循开发封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
封闭:已实现的功能代码块
开放:对扩展开发
如果将开放封闭原则应用在上述需求中,那么就不允许在函数 f1 、f2的内部进行修改代码,老板就给了Low C一个实现方案并说什么时候看懂了什么时候来上班工资20K:
❤单层装饰器(无参)
#!/usr/bin/env python
# -*- coding: utf-8 -*- #装饰器函数
def func(main):
def wra(*args,**kwargs):
#验证代码
return main(*args,**kwargs)
return wra #python语法糖 @func => f1 = func(f1)
@func
def f1(arg):
print('我是F1业务')
if arg == 'f1':
return 'ok' def f2(arg):
print('我是F2业务')
if arg == 'f2':
return 'ok' c = f1("f1")
print(c)
单层装饰器(无参装饰器)
剖析 第一步:
首先@func 是python装饰器的语法糖,这个语句是放在被修饰函数的上方,当出现这个语句就要把它看成 f1 = func(f1)
公式 被装饰函数= 装饰器函数 (被装饰函数) 可以看出被装饰函数此时变成参数被装饰器函数传了进去,所以装饰器函数
(func)的参数main就是 被装饰函数 f1,所以main = f1,并且我们在第三章知道了函数不加括号是不执行的,所以main
就是函数 f1的内存地址,且不会执行。
第二步:
我们发现,装饰器函数func 就是在第三章学的闭包,所以当@func的时候 就是 f1 = func(f1) ,我们要知道函数加上括号就会执行
所以func就执行了,并且因为func是个闭包函数,所以把内部wra函数加载到内存,并返回,所以此时 f1 = wra,并且要注意的
是返回的是wra 没有加括号哦!
第三步:
当看到 c = f1("f1")的时候,因为python语法糖的原因所以f1就是 wra函数也就是图中红色标记箭头的地方,所以f1("f1")就是执行
了wra函数,并且把参数“f1”传了进去,第三章学过动态参数,所以被装饰的函数传入什么参数我们的wra都不需要改,然后看wra函
数内部做了个return main(*args,**kwargs),第一步的时候我们得出了结论 main就是f1函数,所以 main(*args,**kwargs)
,就是执行了f1函数并且把 c = f1("f1") 中的参数“f1”传到的真正的函数 f1里也就是蓝色框圈起来的内容,并且第三章我们也知道了,
传参的时候用 *args,**kwargs 就是把列表和字典分解(不理解请参考第三章解参)。
最后:main(*args,**kwargs) 就是执行了f1函数,当f1函数执行完成后,就return “ok”,所以wra中的 return实际返回的就是
f1函数返回的结果,这也就是c为什么等于“ok”当然就算f1函数没有返回值也无所谓,因为默认会返回None。到此无参装饰器剖析完毕
五、多层装饰器(有参)
咳咳,经过LowC的认真学习,他终于明白了无参装饰器的真意,于是成功的入职了公司,但是随着时间的推移,老大又找到了Low C说
这样我有个需求要你给我改改,我现在呢想在验证之后呢添加一个欢迎功能,这个功能呢,我们业务线的功能想要添加就添加先要不
添加就不添加,要记住封闭原则哦0.0……….
第二天Low C找到了老大说,大哥啊您晚上还是来我家教教我吧,真心的不知道啊0.0,,,于是老大就去了Low C的家里经过一场风云
(此处省略一万个字),最后老大提供了另外的参考代码:
❤多层装饰器(有参)
#!/usr/bin/env python
# -*- coding: utf-8 -*- #欢迎函数
def welcome():
print("欢迎访问") #装饰器函数
def fill(*func):
def single_func(main):
def wra(*args,**kwargs):
#验证代码
if len(func) !=0:
for i in func:
i()
return main(*args,**kwargs)
return wra
return single_func @fill(welcome)#python语法糖 @fill(welcome) => @single_func => f1 = func(f1)
def f1(arg):
print('我是F1业务')
if arg == 'f1':
return '我是f1 ok' @fill()
def f2(arg):
print('我是F2业务')
if arg == 'f2':
return '我是f2 ok' c = f1("f1")
print(c)
print("------------分割线----------")
c2 = f2("f2")
print(c2)
多层装饰器
剖析:第一步
其实有参装饰器,就是无参装饰器加了个闭包,这个闭包的参数接收一个函数,并且使用*func表示这个参数就表示可以传参也可以不传参
@fill(welcome) 我们知道函数加上括号就会执行,所以先执行了装饰器函数fill,并且把welcome函数传给了参数*func,然后我们把subgle_func
当成一个整体,那么这就是个闭包结构,所以当fill()执行后的到了 @fill(welcome) == @single_func *func == welcome这样我们在闭包
内部就可以调用 welcome这个函数!
第二步:
在第一步中得到了 @fill(welcome) == @single_func 那么这就变成了无参装饰器,那么就跟上面讲到的一样 @single_func => f1 = single_func(f1)
所以 f1 = wra函数 main = f1 函数
第三步:
当 f1("f1") 的时候就开始执行wra函数,在wra函数内部我们要先执行*func内的函数,毕竟欢迎信息在前面,所以先执行
并且我们同if判断可以判断使用者是否传入了函数,传入就执行,不传入就不执行,执行完成*func内的函数后就制成执行main
函数(main = f1函数),也就是执行了f1,到此需求实现。
六、冒泡算法
接下来学习一下冒泡排序,冒泡排序的主要思路就是从第一个元素开始,判断这个元素与每一个元素的大小关系,当符合判断关系
后就进行位置替换
def bubble(lists):
count = len(lists)
for i in range(0, count-1):
for j in range(i + 1, count):
if lists[i] > lists[j]:
lists[i], lists[j] = lists[j], lists[i]
print("第%s次排序"%(i+1),lists)
return lists print([5,2,9,1,7,6,8,3,4])
print(bubble([5,2,9,1,7,6,8,3,4]))
冒泡排序
上面的例子中有两层for循环,最内层for循环的作用是比较第一个值与其它每个值的大小,如果大则替换位置,然后拿替换后的值接着、
跟剩下的值进行比较,同理如果大则替换位置,当这一次循环完成后就用第二个值与第二个值后面的值进行比较,如果大则替换位置,
直到只有最后一个值的时候排序就完成了,例子中的列表,让每个元素进行大小值的比较需要循环八次也就是最后是后面两个元素比较
就可以了。
七、代码开发规范
随着Python在国内的发展,使用python编程语言的公司越来越多,在使用python进行开发的时候,不仅仅只是编写代码时要注意规范
而且随着程序的复杂,在格式上也需要有规范,这样你的程序在别人眼中的可读性会大大增加,下面一起来看看编写代码时要注意的事情
缩进
在python代码中缩进的重要是毋庸置疑的,缩进错误会造成程序错误,并且好的缩进在代码可读性上也非常有用
注释
注释是程序非常重要的东西,不管是为了其他人阅读你的代码,还是为了你自己,因为时间久了很多你自己写的程序代码自己都会看不懂
这个时候要快速理解自己的代码,注释就尤为重要,所以你的每一个函数,和后面会学到的类,以及类中的方法,都要给他们加上注释
行的最大长度
每行代码或者输出内容如果过长在显示上和美观上都有较大的影响,所以当一行代码或者内容过长时建议使用 \ 进行换号,推荐将长度限
制在72字符。
软件目录结构
"设计项目目录结构",就和"代码编码风格"一样,属于个人风格问题。目录规范,能更好的控制程序结构,让程序具有更高的可读性,关于如
何组织一个较好的Python工程目录结构,已经有一些得到了共识的目录结构。在Stackoverflow的这个问题上,能看到大家对Python目录结
构的讨论,所以一个程序的目录设计我觉得应该有以下设计,假设你的项目名为foo,最后说一句,目录结构只是更好的让程序有更好的可读
性和易维护性,所以目录结构并非一尘不变,理解其中的意思即可。
作者:北京小远
出处:http://www.cnblogs.com/bj-xy/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
简学Python第四章__装饰器、迭代器、列表生成式的更多相关文章
- 简学Python第三章__函数式编程、递归、内置函数
#cnblogs_post_body h2 { background: linear-gradient(to bottom, #18c0ff 0%,#0c7eff 100%); color: #fff ...
- 简学Python第五章__模块介绍,常用内置模块
Python第五章__模块介绍,常用内置模块 欢迎加入Linux_Python学习群 群号:478616847 目录: 模块与导入介绍 包的介绍 time &datetime模块 rando ...
- 简学Python第六章__class面向对象编程与异常处理
Python第六章__class面向对象编程与异常处理 欢迎加入Linux_Python学习群 群号:478616847 目录: 面向对象的程序设计 类和对象 封装 继承与派生 多态与多态性 特性p ...
- 简学Python第七章__class面向对象高级用法与反射
Python第七章__class面向对象高级用法与反射 欢迎加入Linux_Python学习群 群号:478616847 目录: Python中关于oop的常用术语 类的特殊方法 元类 反射 一.P ...
- 流畅的python第七章函数装饰器和闭包学习记录
本章讨论的话题 python如何计算装饰器句法 python如何判断变量是不是局部的(通过函数内部是否给变量赋值过来判断是否是局部变量) 闭包存在的原因和工作原理(闭包是一种函数,它会保留定义函数时存 ...
- 简学Python第二章__巧学数据结构文件操作
#cnblogs_post_body h2 { background: linear-gradient(to bottom, #18c0ff 0%,#0c7eff 100%); color: #fff ...
- Python第五章__模块介绍,常用内置模块
Python第五章__模块介绍,常用内置模块 欢迎加入Linux_Python学习群 群号:478616847 目录: 模块与导入介绍 包的介绍 time &datetime模块 rando ...
- Python 标准库中的装饰器
题目描述 1.简单举例 Python 标准库中的装饰器 2.说说你用过的 Python 标准库中的装饰器 1. 首先,我们比较熟悉,也是比较常用的 Python 标准库提供的装饰器有:property ...
- Python基础(八)装饰器
今天我们来介绍一下可以提升python代码逼格的东西——装饰器.在学习装饰器之前我们先来复习一下函数的几个小点,方便更好的理解装饰器的含义. 一.知识点复习 1, 在函数中f1和f1()有什么不同,f ...
随机推荐
- UVa 10057 - A mid-summer night's dream
题目大意:给n个数,找一个数A使得A与这n个数的差的绝对值最小.输出A最小的可能值,n个数中满足A的性质的数的个数以及满足A性质的不同的数的个数(不必从这n个数中挑选). 看见绝对值就想到了数轴上点之 ...
- linux 同步机制之complete【转】
转自: http://blog.csdn.net/wealoong/article/details/8490654 在Linux内核中,completion是一种简单的同步机制,标志"thi ...
- 25+免费的Bootstrap HTML5网站模板
在前端框架中,Bootstrap可以说是非常有名的高级网站设计框架.网上也有很多使用Bootstrap程序创建的免费模板.这些模板设计成响应式模式,因此你可以使用它们来为所有的设备平台和浏览器创建网站 ...
- 3.2. 添加模板版本(Core Data 应用程序实践指南)
为了不像3.1那样崩溃,修改模型之前先创建新的模型版本.添加之后,会生成一个新的xcdatamodel文件,并且跟原来的内容完全一样,这有意思了,但是不要删除原来旧版的模型.旧的模型有助于把原来持久化 ...
- PHP实现验证码图片
<?php header("Content-type: image/png"); session_start(); $authnum = ''; $str = 'abcdef ...
- jQuery动画高级用法(上)——详解animation中的.queue()动画队列插队函数
决定对animate方面做一些总结,希望能给大家一些启发和帮助 从一个实际应用谈起 今天不谈animate().fadeIn().fadeOut().slideUp().show().hide()诸如 ...
- iOS 之 alcatraz (插件管理器)
1. 安装 1.1. 打开命令行 curl -fsSL https://raw.githubusercontent.com/supermarin/Alcatraz/deploy/Scripts/ins ...
- 建立、配置和使用Activity——Activity
Activity是Android应用中最重要.最常见的应用组件(此处的组件是粗粒度的系统组成部分,并非指界面控件:widget).Android应用的一个重要组成部分就是开发Activity,下 面将 ...
- java算法 蓝桥杯 高精度加法
问题描述 在C/C++语言中,整型所能表示的范围一般为-231到231(大约21亿),即使long long型,一般也只能表示到-263到263.要想计算更加规模的数,就要用软件来扩展了,比如用数组或 ...
- TFS 测试用例导入、导出工具
TFS的测试管理提供了测试规划.创建.运行以及进度跟踪等功能.测试人员通过浏览器就几乎可以完成手个测试的全部过程. 用过TFS测试用例的朋友们,很多人应该都知道,在TFS的Portal中以及相应的数据 ...