day04 函数进阶
文章目录
- 1.内容回顾
- 1. 文件操作
- 2. 工作目录
- 3. 初始函数
- 2.今日内容
- 1. 装饰器
- 1.2 函数进阶
- 1.3 闭包
- 1.4 装饰器
- 1.5 什么情况下用,不改变代码
- 1.6 装饰器的固定格式
- 1.7 一个函数被多个装饰器装饰
- 1.8 带参数的装饰器
- 2. 迭代器
- 3. 生成器
- 3.1 列表推导式
- 3.2 生成器表达式
- 4. 总结
- 5. 匿名函数
- 5.1 三元运算符
- 6. 递归函数
1.内容回顾
1. 文件操作
-
打开文件
-
open(‘文件路径’,mode=‘r’,encoding=‘utf-8’)
-
open(‘文件路径’,mode=‘w’,encoding=‘utf-8’)
-
open(‘文件路径’,mode=‘a’,encoding=‘utf-8’)
-
注:一般不用r+或w+,pycharm创建的文件是utf-8,Windows创建的是gbk模式,以什么方式创建以什么方式打开
-
open(‘文件路径’,mode=‘rb’)
-
open(‘文件路径’,mode=‘wb’)
-
open(‘文件路径’,mode=‘ab’)
f = open('test',mode='rb') content = f.read() #只读 print(content) msg = content.decode('utf-8') #转码 print(msg) f.close()f = open('test',encoding='utf-8') #读+转码 content = f.read() print(content)
-
-
[外链图片转存失败(img-uhjVlgcT-1564886425126)(C:\Users\贡金敏\AppData\Roaming\Typora\typora-user-images\1564279377373.png)]
-
读写文件
- 读
- read() #默认读所有
- read(n) #读n个字节/字符,处理视频时
- readline() #每次读一行,会记录文件指针,不知道什么时候结束
- for line in f:
- 写
- write(‘想写啥写啥’)
- write(b’abc’) #wb写字节,如果以rb方式打开必须这么用
- 读
-
关闭文件
- f.close()
-
删除和修改 #关闭文件之后再删除
- import os
- os.remove(‘文件的绝对路径’)
- os.rename(‘原名’,‘目的名’)
- 修改文件
- 读a文件,写b文件
- 删除a,重命名b–>a
2. 工作目录
-
在哪个路径下执行Python程序,工作目录就是哪里
-
所以 如果希望在任何一个位置执行代码都不出现文件目录找不到的情况,就要用绝对路径,而不是相对路径
path = r'G:\py27\day04\test' f = open(path,encoding = 'utf-8') content = f.read() print(content)
-
pycharm下会有很多优化机制
- 在执行代码的时候,会自动的把当前所在的文件目录作为工作目录
3. 初始函数
-
函数的定义
- def 函数名(形参):
- 缩进 函数体
- return 返回值
- def 函数名(形参):
-
调用
- 变量 = 函数名(实参)
- 变量就是函数的返回值
-
返回值
- 不写return 默认返回None #None表示空
- 只写return 表示函数结束,默认返回None
- return 值 值被返回给调用者
- return 值1,值2 接收的返回值是元组(值1,值2)
-
参数
-
站在调用者的角度上
-
按照位置传参数
def cal(a,b,c):
pass
cal(1,2,3)* *可以迭代的对象(可以被for循环)```def cal(a,b,c):passl = [1,2,3]cal(*l) #-->cal(1[0],1[1],1[2])```
-
-
按照关键字传参
def cal(a,b,c):pass cal(a = 1,b = 2,c = 3)
-
**字典
def cal(a,b,c):pass d = {'a':1,'b':2,'c':3} cal(**d) #按照关键字传参,字典特有
-
混合:先位置,在关键字
def cal(a,b,c):pass cal(1,2,c = 3)
-
-
站在定义函数的角度上(定义参数要按以下顺序)
-
位置参数
-
*args 动态参数 #默认参数不能再动态参数之前
-
默认参数
-
**kwargs 动态参数
def cal(a,b,*args,c = 10,d = '男',**kwargs) #按照这个顺序调用,中间的默认参数可以设置多个
-
-
参数的陷阱,如果默认参数的值是一个可变数据类型
-
那么所有的调用者都共享这一个数据
-
这个变量是在定义的时候创建1次,而不会在调用的过程中再次被创建了
def func(l = []): #这个列表在定义的时候已经被创建出来,再次调用不会再创建了l.append(1)print(l)func() func() 结果:
[1]
[1,1]def func(a,l = []):
print(id(l))
l.append(a)
print(l)
return lfunc(1) #没传传参数就用默认列表
func(2,[]) #因为传了[]就往[]里传参数
func(3) #没传参数,就用默认列表就放3
func(4,[]) # [4]def func(a,l = []):
print(id(l))
l.append(a)
print(l)
return lfunc(1)
l1 = func(2,[])
func(3)
func(4,[]) # [4] -
2.今日内容
1. 装饰器
-
import time #时间模块
- 被人写好的一些功能,放在一个模块里
- 和时间相关的功能,就放在了time模块
import time print(time.time()) 1564282438.9430697 # 格林威治时间 - 19700101 08:0:00 北京 # 格林威治时间 - 19700101 00:0:00 伦敦 #1564282438.9430697 #1564282488.7851577 #时间模块一般用来计算某个程序所用的时间,效率测试
print('start') time.sleep(1) print('end')
import time def func():sum_num = 0for i in range(100000):sum_num += i print(time.time()) func() print(time.time())改进 import time def func():sum_num = 0for i in range(100000):sum_num += i start = time.time() func() print(time.time()-start)
1.2 函数进阶
-
函数名赋值
def a():print('in a func')a() print(a) b = a print(b) b() # 并不是只有函数名()才能调用一个函数 #只要是函数的地址对应的变量都可以通过()调用函数 l = [a] #a存的是内存地址 print(l[0]) #这个也是指向a的内存地址 l[0]() #所以得出的结果还是in a func # 实际上函数的名字可以被赋值,也可以作为一个容器类型的元素 # 变量怎么使用,函数的名字都可以怎么使用
-
函数的名字做参数
def a():print('in a func') def wahaha(f):print(f)f() wahaha(a)
-
函数的名字做返回值
def wahaha():def a():print('in func a')return a funca = wahaha() #函数a作为wahaha的返回值赋予给了funca funca() #调用funca()就返回了函数a,其中内存地址都指向同一个地方
1.3 闭包
-
闭包:如果一个内部的函数引用了外部函数的变量,那么这个内部函数就是一个闭包函数def wahaha():
name = 'alex'def a():#'''一旦内层函数引用了外层函数的变量,a就是一个闭包函数'''print('in a func',name)return a a = wahaha() a() print(a.__closure__) #a.__closure__不是空元祖,a就是闭包函数,如果没有引用外部变量就打印None
#不是闭包函数,因为inner中没有用到a,b变量 def func():a = 1b = 2def inner()print(1,2)
# inner中虽然用到a,b,但是用到的是自己的参数a,b,而不是外部的变量所以不是闭包 def func():a = 1b = 2def inner(a,b)print(a,b)
def func2():a = 1b = 2def inner(): #是闭包print(a,b)
def func2(a,b):def inner(): #是闭包print(a,b)
a = 1 b = 2 def func2():def inner(): #不是闭包print(a,b)
-
闭包的作用:需要把某一个数据存在局部作用域里,既不被污染又能够时时刻刻能重复使用,不用重复创建的时候,使用闭包
#请求一个网页 from urllib import request # ret = request.urlopen('') # print(ret.read().decode('utf-8')) def get_source_html(url):ret = request.urlopen(url)return ret.read().decode('utf-8') ret = get_source_html('/') print(ret)
#请求一个网页 from urllib import request def get_source_html(url):dic = {}def get_url():if url in dic:return dic[url]else:ret = request.urlopen(url)dic[url]=retreturn ret.read().decode('utf-8')return get_url get_url = get_source_html('/') print(get_url()) print(get_url()) print(get_url())
1.4 装饰器
-
计算函数效率
# import time # def timmer(funcname): # start = time.time() # funcname() # print(time.time()-start) # def func(): # sum_num = 0 # for i in range(100000): # sum_num += i # def func2(): # sum_num = 0 # for i in range(100000): # sum_num += i # # timmer(func) # timmer(func2)
改进一: import time #用到了闭包,函数作为返回值,函数作为参数 def timmer(funcname): #funcname就是'func的内存地址'def inner(*args,**kwargs):start = time.time()funcname(*args,**kwargs)print(time.time()-start)return innerdef func(n,m):sum_num = 0for i in range(n,m):sum_num += iprint('sum_num:',sum_num) def func2():sum_num = 0for i in range(100000):sum_num += iprint('in func2',sum_num)func = timmer(func) #func = inner的内存地址 func(n = 100000000,m = 20) #想调用func,实际调的inner func2 = timmer(func2) func2()
改进二: import time #用到了闭包,函数作为返回值,函数作为参数 def timmer(funcname): #funcname就是'func的内存地址'def inner(*args,**kwargs):start = time.time()ret = funcname(*args,**kwargs)print(time.time()-start)return retreturn innerdef func(n,m):sum_num = 0for i in range(n,m):sum_num += ireturn sum_num def func2():sum_num = 0for i in range(100000):sum_num += ireturn sum_numfunc = timmer(func) #func = inner的内存地址 ret = func(100000,20) #想调用func,实际调的inner func2 = timmer(func2) ret2 = func2() print(ret,ret2)
[外链图片转存失败(img-9H85k0ie-1564886425127)(C:\Users\贡金敏\AppData\Roaming\Typora\typora-user-images\1564640440071.png)]
1.5 什么情况下用,不改变代码
-
@timmer能够让函数快速加上装饰器
import time #用到了闭包,函数作为返回值,函数作为参数 def timmer(funcname): #funcname就是'func的内存地址'def inner(*args,**kwargs):start = time.time()ret = funcname(*args,**kwargs)print(time.time()-start)return retreturn inner@timmer #就是func = timmer(func) #func = inner的内存地址 def func(n,m):sum_num = 0for i in range(n,m):sum_num += ireturn sum_num@timmer #func2 = timmer(func2) #func2 = inner的内存地址 def func2():sum_num = 0for i in range(100000):sum_num += ireturn sum_numret = func(100000,20) #想调用func,实际调的inner ret2 = func2() print(ret,ret2)
-
什么情况下用
-
在已经写好发版的程序的基础上,需要对一个函数执行前后增加功能的时候
- 开放封闭原则
- 开放:对扩展是开放的(增加新功能)
- 封闭:对修改时封闭(可以修改装饰器,但内部函数不要修改)
- 开放封闭原则
-
有的时候也会写好一些装饰器,加在需要装饰的函数上面
-
login Django框架里也这么用
-
log
使用场景 #用户登录 # def login(): # pass # # @login # def lianxi(): # pass # # @login # def gaunli(): # pass # # @login # def xinsuibi(): # pass#论坛程序 # def log(): # pass # # @log # def 发布帖子(): # pass # # @log # def 评论(): # pass
-
-
1.6 装饰器的固定格式
def 装饰器的名字(被装饰的函数名func):def inner(*args,**kwargs): #wahaha参数也传给inner'''在执行被装饰的函数之前要做的事''''''判断是否登录'''ret = func(*args,**kwargs) #inner之后传给func'''在执行被装饰的函数之后要做的事''''''写log'''return ret #调用wahaha返回wahaha的返回值return inner #inner没有括号@装饰器的名字
def wahaha():pass
wahaha() #调用wahaha实际上调用的是inner
[外链图片转存失败(img-UyW3EhYv-1564886425128)(C:\Users\贡金敏\AppData\Roaming\Typora\typora-user-images\1564642148031.png)]
-
函数名.name
def register(name):print('%s注册'%name) print([register.__name__])
-
用户登录
import time def wapper(func):def inner(*args,**kwargs):ret = func(*args,**kwargs)t = time.strftime('%Y-%m-%d %H:%M:%S')print('在%s[%s]执行了%s函数'%(t,args[0],func.__name__))return retreturn inner@wapper def login(name):print('%s登录'%name)@wapper def register(name):print('%s注册'%name) # print([register.__name__])login('alex') register('alex')
-
将用户登录信息记录到operate.log日志文件里
import time def wapper(func):def inner(*args,**kwargs):ret = func(*args,**kwargs)t = time.strftime('%Y-%m-%d %H:%M:%S')with open(r'operate.log',mode='a',encoding='utf-8') as f:f.write('\n在%s[%s]执行了%s函数'%(t,args[0],func.__name__))return retreturn inner@wapper def login(name):print('%s登录'%name)@wapper def register(name):print('%s注册'%name) # print([register.__name__])login('alex') register('alex')
改进 import time from functools import wraps def wapper(func): #告诉装饰器装饰的是func@wraps(func)def inner(*args,**kwargs):ret = func(*args,**kwargs)t = time.strftime('%Y-%m-%d %H:%M:%S')with open(r'operate.log',mode='a',encoding='utf-8') as f:f.write('\n在%s[%s]执行了%s函数'%(t,args[0],func.__name__))return retreturn inner@wapper def login(name):print('%s登录'%name)@wapper def register(name):print('%s注册'%name) print([register.__name__])login('alex') register('alex')
-
函数的注释(独立的函数都要写注释)
def func(a,b):'''函数的注释:这个函数是做什么用的:param a: 表示一个参数 int 长方形的宽:param b: int 长方形的长:return: int 长方形的面积'''return a+b#param 参数 形参# args agrguments 实参print(func.__doc__)
-
1.7 一个函数被多个装饰器装饰
-
装饰器越离函数进越放在里面
def wrapper1(func): #func = inner2def inner(*args,**kwargs):print('before inner1')ret = func(*args,**kwargs)print('after inner1')return retreturn inner def wrapper2(func): # func = wahahadef inner2(*args,**kwargs):print('before inner2')ret = func(*args,**kwargs)print('after inner2')return retreturn inner2 @wrapper1 #wahaha = wrapper1(inner2) = inner1 @wrapper2 #wahaha = wrapper2(wahaha) = inner2 def wahaha():print('WAHAHA')wahaha() #inner1
[外链图片转存失败(img-vd2gKJWR-1564886425130)(C:\Users\贡金敏\AppData\Roaming\Typora\typora-user-images\1564645131061.png)]
-
记录登录时间逻辑
# timmer 装饰器 计算被装饰的函数的执行时间 # login装饰器 检测被装饰的函数是不是登录,如果没有登录要先登录才能使用这个函数 # timmer 装饰器永远紧贴着被装饰的函数 # login 装饰器 没有特殊需求可以任意排列 # logger 装饰器 没有特殊需求可以任意排列@login @timmer #所以timmer贴近函数 def get_html():pass # 先判断登录 #如果没登录#调用登录 input() #输入20s # 如果登录了# get_html --被装饰的函数 # 先记录当前时间 = 登录 + get_html #get_html() #记录结束时间
1.8 带参数的装饰器
-
计算时间
报错,以后修正 ''' /usr/bin/env python -*- conding:utf-8 -*- ----gongjinmin---- '''# 带参数的装饰器 # flask框架中 路由里用到了带参数的装饰器 import time flag = False def timmer(flag): #flag = Truedef outer(func): # func =wahahadef inner(*args,**kwargs):if flag:start = time.time()ret = func(*args,**kwargs)print(time.time()-start)else:ret = func(*args, **kwargs)return retreturn innerreturn outer@timmer(True) #@outer = wahaha = outer(wahaha) def wahaha():print('in wahaha')@timmer def wahaha1(flag):print('in wahaha1')@timmer def wahaha2(flag):print('in wahaha2')@timmer def wahaha3(flag):print('in wahaha3')wahaha() wahaha1() wahaha2() wahaha3()
-
登录改进
''' /usr/bin/env python -*- conding:utf-8 -*- ----gongjinmin---- ''' import time from functools import wraps def log(filename):def wapper(func): #告诉装饰器装饰的是func@wraps(func)def inner(*args,**kwargs):ret = func(*args,**kwargs)t = time.strftime('%Y-%m-%d %H:%M:%S')with open(filename,mode='a',encoding='utf-8') as f:f.write('\n在%s[%s]执行了%s函数'%(t,args[0],func.__name__))return retreturn innerreturn wapper@log('login.log') def login(name):print('%s登录'%name)@log('register.log') def register(name):print('%s注册'%name) print([register.__name__])login('alex') register('alex')
2. 迭代器
-
迭代器
# 需要一个从1-100000000的数字序列 # py2和py3的区别 #py2 range(100000000)真的在内存中生成100000000个数#xrange(100000000) 相当于py3并不是在执行的时候生成100000000个数 #py3 range(100000000) 并不是在执行的时候生成100000000个数
ran = range(100) print(ran) #找ran拿数字了 # ran变成了一个迭代器 iterator = ran.__iter__() #获取迭代器 print(iterator) print(iterator.__next__()) print(iterator.__next__()) print(iterator.__next__()) #迭代器只记当前的数,获取下一个数就加 #迭代器能够从一个数据里面一个一个的取值并且这个数据里取出的所有的值在没取之前不占内存
-
迭代器
- 一个一个的取值,而不是一次性把所有数据都创建出来,迭代器中的数据不取不创建
- 只能按照顺序取,不能跳过也不能回头
- 一个迭代器中的数据只能从头到尾取一次
-
可迭代器:如果一个数据类型中有iter方法,那么这个数据就是可迭代类型
-
迭代器协议:如果一个数据类型中有iter和next方法,那么这个数据类型就是一个迭代器类型
-
可迭代器协议包含迭代器协议
#可迭代的 print(dir()) #查看方法 print('__iter__' in dir('abc')) #字符串里有iter方法 print('__iter__' in dir(123)) #False print('__iter__' in dir(123.43)) #False print('__iter__' in dir({1,2,3})) #True print('__iter__' in dir((1,2,3))) #True print('__iter__' in dir({1:3})) #True f = open(r'G:\py27\其他脚本\register.log',encoding='utf-8') print('__iter__' in dir(f)) #True f.close() print('__iter__' in dir(range(10))) #True
#迭代器 print('__next__' in dir('abc')) #False #对于基础数据类型来说都不是迭代器,只是可迭代类型 f = open(r'G:\py27\其他脚本\register.log',encoding='utf-8') print('__next__' in dir(f)) #True f.close() #文件操作符是一个迭代器
-
迭代器的特点:节省内存空间
-
除了文件操作符以外你见过的所有内容都不是迭代器类型
都只是可迭代类型 -
可迭代类型是可以通过某些方法转换成迭代器的
文件操作符节省空间 f = open(r'G:\py27\其他脚本\register.log',encoding='utf-8') for line in f:print(line) #一行行打印,打印第二行时第一行占用的内存空间会被回收 f.close()
-
-
可迭代类型.__iter__方法可以把可迭代类型转换成迭代器
l = [1,2,3,4,5,6] iter = l.__iter__() print(iter.__next__()) print(iter.__next__()) print(iter.__next__()) print(iter.__next__()) print(iter.__next__()) print(iter.__next__()) print(iter.__next__())
for循环的基本运行原理 l = [1,2,3,4,5,6] num = l.__iter__() while True:try:print(num.__next__())except StopIteration:break
-
所有能被for循环的至少是一个可迭代类型
-
今后如果我说xxx是一个迭代器,知道这个xxx并不是直接存储了内容,而是需要for循环才能获取每一个值
拿衣服: def make_cloth(n):clothes = []for i in range(n):clothes.append('第%s件衣服'%i)return clothes cloth_lst = make_cloth(100) print(cloth_lst)改进:生成器 def make_cloth_simple(n):'''生成器函数,没有return 只有yield:param n::return:'''print('做第一件衣服')yield 1print('做第二件衣服')yield 2print('做第三件衣服')yield 3ret =make_cloth_simple(4) print(ret) #generator 生成器 生成器 生成器的本质就是一个迭代器 # print(ret.__next__()) # print(ret.__next__()) # print(ret.__next__()) for i in ret:print(i)
3. 生成器
-
生成器
-
作用:写生成器函数主要是为了不断的从一个容器里面或一个规则里不断的取需要的一个个数据,既能节省内存又能完成循环或迭代
def make_cloth_simple(n):for i in range(n):yield '第%s件衣服'%i #会记住执行到哪里 ret = make_cloth_simple(10000) print(ret) for n in range(20):print(ret.__next__()) print('='*50) for i in range(50): #接下来在执行会从之前执行的开始print(ret.__next__())
-
示例
#监听文件内的输入 #在python中监听一个文件的输入事件,只要用户输入了新内容,就打印到屏幕中来 with open(r'G:\py27\其他脚本\test',mode='r',encoding='utf-8') as f:while True:ret = f.readline()if ret: #如果ret有值print(ret) #打印 改进版: def listen():with open(r'G:\py27\其他脚本\test',mode='r',encoding='utf-8') as f:while True: #可能执行了2000次循环ret = f.readline().strip()if 'error'in ret: #如果ret有值,可以过滤出error错误日志yield ret #生成器,特点是暂停 for ret in listen(): #而for只有拿到了真实的值才会打印print(ret)改进:程序写入 while True:with open(r'G:\py27\其他脚本\test',mode='a',encoding='utf-8') as f:content = input('>>>')f.write(content)程序读取: def listen():with open(r'G:\py27\其他脚本\test',mode='r',encoding='utf-8') as f:while True: #可能执行了2000次循环ret = f.readline().strip()if 'error' in ret: #如果ret有值yield ret #生成器,特点是暂停 for ret in listen(): #而for只有拿到了真实的值才会打印print(ret)
注意:Linux和Mac不支持手动输入数据再读取,只支持程序输入和程序读取
[外链图片转存失败(img-dmQ6VDl1-1564886425131)(C:\Users\贡金敏\AppData\Roaming\Typora\typora-user-images\1564721892801.png)]
# 将来你写的所有的代码 最好都把读文件的操作写成一个生成器 #代码紧耦合 def login():user = input('username:')pwd = input('password:')with open(r'G:\py27\其他脚本\test',mode='r',encoding='utf-8') as f:for line in f:usr,passwd=line.strip().split('|')if user == usr and pwd == passwd:print('登录成功')breakelse:print('登陆失败') login()
#拆分代码思维逻辑 #登录#读文件 #注册#检测是不是已经存在相同的用户名 一、 def get_user(file):line_lst = []with open(file,mode='r',encoding='utf-8') as f:for line in f:usr,pwd = line.strip().split('|')line_lst.append((usr,pwd))return line_lstdef login():user = input('username:')passwd = input('password:')for usr,pwd in get_user('test'):if usr == user and pwd == passwd:print('登录成功')breakelse:print('登陆失败') login()
改进二: def get_user(file):with open(file,mode='r',encoding='utf-8') as f:for line in f:usr,pwd = line.strip().split('|')yield usr,pwddef login():user = input('username:')passwd = input('password:')for usr,pwd in get_user('test'):if usr == user and pwd == passwd:print('登录成功')breakelse:print('登陆失败') login()
-
注意
- 所有的读文件和写文件 尽量都拆分到其他函数中
- 不要嵌到你写好的功能函数中
- 比如 登录 函数中不要写读文件的逻辑
- 比如 注册 函数中不要写读\写文件的逻辑
-
生成器:特点:一个生成器中的数据也是只能从头到尾取一次
def func():for i in range(10):yield 'a%s'%i g = func() #对同一个生成器迭代 for i in g:print(i) for i in g:print(i) 结果: 生成10个数
def func(): for i in range(10):yield 'a%s'%i for i in func(): #每次生成一个新的生成器迭代print(i) for i in func():print(i) 结果: 生成20个数
def func():for i in range(10):yield 'a%s'%i g1 = g2 =func() #还是同一个生成器 for i in g1:print(i) for i in g2:print(i) 结果: 生成10个数
def func():for i in range(10):yield 'a%s'%i g1 = func() #两个生成器 g2 =func() for i in g1:print(i) for i in g2:print(i) 结果: 生成20个数
3.1 列表推导式
-
示例
l = [1,2,3,4,5,6] ls = [] for i in l :ls.append(i*2) print(ls)改进 l = [1,2,3,4,5,6] ls = [i*2 for i in l] print(ls)
# 所有的偶数都放到新的列表中 l = [1,2,3,4,5,6] ls = [ i for i in l if i%2 == 0] print(ls)
# 30以内所有能被3整除的数 ls = [i for i in range(30) if i%3 == 0] print(ls)
#取 100以内的数 ls = [ i for i in range(100)] print(ls)
# 30以内所有能被3整除的数的平方 ls = [i*i for i in range(30) if i%3 == 0] ls = [i**2 for i in range(30) if i%3 == 0] print(ls)
# 找到嵌套列表中名字含有两个‘e’的所有名字 names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]ls = [name for lst in names for name in lst if name.count('e') == 2 ] print(ls)
3.2 生成器表达式
-
生成器表达式
-
列表推导式的[] 换成() 就变成了生成器表达式了
lst = [i*2 for i in range(5)] print(lst) # lst中的所有元素已经在内存里了gen = (i*2 for i in range(5)) print(gen) # gen里什么也没有##三种取值方式 #gen.__next__() #for n in gen:pass #list(gen)
gen = (i*2 for i in range(5)) print(gen) print('--->',gen.__next__()) for i in gen:print(i)
#报错StopIteration gen = (i*2 for i in range(5)) print(gen) list(gen) #数字在这里被取完了 print('--->',gen.__next__()) for i in gen:print(i)
gen = (i*2 for i in range(5)) print(gen) print('--->',gen.__next__()) print(list(gen)) for i in gen:print(i)
-
-
区别:
- 生成器元素永远需要取才能有不取就没有
- 列表推导式一旦写好元素就已经在了
4. 总结
-
迭代器 是一个可以被for循环的节省内存的类型
-
生成器 是程序员能够自己写的迭代器(生成器本质是迭代器)
- 生成器函数 yield
- g = 生成器函数()
- 生成器表达式
- g = (表达式)
- 列表推导式和生成器表达式所使用的的 “表达式是相同的”
- 生成器函数 yield
-
所有的生成器都符合迭代器的特点
- 1.一个一个的取值,而不是一次性把所有数据都创建出来,迭代器中的数据不取不创建
- 2.只能按照顺序取,不能跳过也不能回头
- 3.一个迭代器中的数据只能从头到尾取一次
-
从迭代器中取值的三种方式
- 1.gen.next
- 2.for n in gen:pass
- 3.list(gen)
-
简单的for循环生成新列表 -->列表推导式联系起来
-
只要用到列表推导式 --> 把列表推导式转成生成器表达式
-
生成器函数 --> 处理文件
-
面试题一 : 生成器函数 + 生成器表达式
- 生成器中的值只能从头到尾取一次
def demo():for i in range(4):yield ig=demo()g1=(i for i in g) g2=(i for i in g1)print(list(g1)) print(list(g2))
[外链图片转存失败(img-MSJD3GHR-1564886425131)(C:\Users\贡金敏\AppData\Roaming\Typora\typora-user-images\1564729498098.png)]
-
面试题二:重点内容 : 生成器 + for循环
- for循环一定要拆
- 生成器代码不取值不调用
def add(n,i):return n+idef test():for i in range(4):yield ig=test() for n in [1,10]:g=(add(n,i) for i in g)print(list(g)) #解析 n=1 n10 g = (add(n,i) for i in add(n,i) for i in test()) g = (add(n,i) for i in add(n,i) for i in (0,1,2,3)) g = (add(n,i) for i in (10,11,12,13)) g = (20,21,22,23)
[外链图片转存失败(img-wvZLd2GF-1564886425132)(C:\Users\贡金敏\AppData\Roaming\Typora\typora-user-images\1564729791765.png)]
5. 匿名函数
5.1 三元运算符
-
示例
a = 10 b = 15 max_num = 0 if a>b:max_num = a else:max_num = b max_num = a if a>b else b print(max_num)
-
匿名函数:
- 如果一个函数中的功能非常小,只有一句代码
- 这个时候我们就可以把这个函数创建成一个匿名函数
- lambda表达式 == 匿名函数 (所有匿名函数用lambda表达)
def func1(a,b):return a+b ret1 = func1(1,2) print(ret1)匿名函数: func2 = lambda a,b:a+b ret2 = func2(3,4) print(ret2)
def func1(a,b):return a+bfunc2 = lambda a,b:a+bprint(func1.__name__) print(func2.__name__) 结果: func1 <lambda>
-
简单到一句话可以描述清楚才用lambda表达式
lambda表达式 a,b两个值,求比较大的值 lambda表达式 a为参数,求a的奇\偶性 lambda表达式 a为参数,求a的绝对值func1 = lambda a,b : a if a>b else b ret = func1(10,2) print(ret)func2 = lambda a : '偶数' if a%2 == 0 else '奇数' ret = func2(11) print(ret)func3 = lambda a:a if a>0 else -a ret = func3(-2) print(ret) ret = func3(6) print(ret)func = lambda :2+3+4 #不传参,比较少见,lambda没有return ret = func() print(ret)
6. 递归函数
g)
print(list(g))
#解析
n=1
n10
g = (add(n,i) for i in add(n,i) for i in test())
g = (add(n,i) for i in add(n,i) for i in (0,1,2,3))
g = (add(n,i) for i in (10,11,12,13))
g = (20,21,22,23)
[外链图片转存中...(img-wvZLd2GF-1564886425132)]### 5. 匿名函数#### 5.1 三元运算符* 示例
a = 10
b = 15
max_num = 0
if a>b:
max_num = a
else:
max_num = b
max_num = a if a>b else b
print(max_num)
* 匿名函数:* 如果一个函数中的功能非常小,只有一句代码
* 这个时候我们就可以把这个函数创建成一个匿名函数
* lambda表达式 == 匿名函数 (所有匿名函数用lambda表达)
def func1(a,b):
return a+b
ret1 = func1(1,2)
print(ret1)
匿名函数:
func2 = lambda a,b:a+b
ret2 = func2(3,4)
print(ret2)
def func1(a,b):
return a+b
func2 = lambda a,b:a+b
print(func1.name)
print(func2.name)
结果:
func1
* 简单到一句话可以描述清楚才用lambda表达式
lambda表达式 a,b两个值,求比较大的值
lambda表达式 a为参数,求a的奇\偶性
lambda表达式 a为参数,求a的绝对值
func1 = lambda a,b : a if a>b else b
ret = func1(10,2)
print(ret)
func2 = lambda a : ‘偶数’ if a%2 == 0 else ‘奇数’
ret = func2(11)
print(ret)
func3 = lambda a:a if a>0 else -a
ret = func3(-2)
print(ret)
ret = func3(6)
print(ret)
func = lambda :2+3+4 #不传参,比较少见,lambda没有return
ret = func()
print(ret)
### 6. 递归函数
day04 函数进阶
文章目录
- 1.内容回顾
- 1. 文件操作
- 2. 工作目录
- 3. 初始函数
- 2.今日内容
- 1. 装饰器
- 1.2 函数进阶
- 1.3 闭包
- 1.4 装饰器
- 1.5 什么情况下用,不改变代码
- 1.6 装饰器的固定格式
- 1.7 一个函数被多个装饰器装饰
- 1.8 带参数的装饰器
- 2. 迭代器
- 3. 生成器
- 3.1 列表推导式
- 3.2 生成器表达式
- 4. 总结
- 5. 匿名函数
- 5.1 三元运算符
- 6. 递归函数
1.内容回顾
1. 文件操作
-
打开文件
-
open(‘文件路径’,mode=‘r’,encoding=‘utf-8’)
-
open(‘文件路径’,mode=‘w’,encoding=‘utf-8’)
-
open(‘文件路径’,mode=‘a’,encoding=‘utf-8’)
-
注:一般不用r+或w+,pycharm创建的文件是utf-8,Windows创建的是gbk模式,以什么方式创建以什么方式打开
-
open(‘文件路径’,mode=‘rb’)
-
open(‘文件路径’,mode=‘wb’)
-
open(‘文件路径’,mode=‘ab’)
f = open('test',mode='rb') content = f.read() #只读 print(content) msg = content.decode('utf-8') #转码 print(msg) f.close()f = open('test',encoding='utf-8') #读+转码 content = f.read() print(content)
-
-
[外链图片转存失败(img-uhjVlgcT-1564886425126)(C:\Users\贡金敏\AppData\Roaming\Typora\typora-user-images\1564279377373.png)]
-
读写文件
- 读
- read() #默认读所有
- read(n) #读n个字节/字符,处理视频时
- readline() #每次读一行,会记录文件指针,不知道什么时候结束
- for line in f:
- 写
- write(‘想写啥写啥’)
- write(b’abc’) #wb写字节,如果以rb方式打开必须这么用
- 读
-
关闭文件
- f.close()
-
删除和修改 #关闭文件之后再删除
- import os
- os.remove(‘文件的绝对路径’)
- os.rename(‘原名’,‘目的名’)
- 修改文件
- 读a文件,写b文件
- 删除a,重命名b–>a
2. 工作目录
-
在哪个路径下执行Python程序,工作目录就是哪里
-
所以 如果希望在任何一个位置执行代码都不出现文件目录找不到的情况,就要用绝对路径,而不是相对路径
path = r'G:\py27\day04\test' f = open(path,encoding = 'utf-8') content = f.read() print(content)
-
pycharm下会有很多优化机制
- 在执行代码的时候,会自动的把当前所在的文件目录作为工作目录
3. 初始函数
-
函数的定义
- def 函数名(形参):
- 缩进 函数体
- return 返回值
- def 函数名(形参):
-
调用
- 变量 = 函数名(实参)
- 变量就是函数的返回值
-
返回值
- 不写return 默认返回None #None表示空
- 只写return 表示函数结束,默认返回None
- return 值 值被返回给调用者
- return 值1,值2 接收的返回值是元组(值1,值2)
-
参数
-
站在调用者的角度上
-
按照位置传参数
def cal(a,b,c):
pass
cal(1,2,3)* *可以迭代的对象(可以被for循环)```def cal(a,b,c):passl = [1,2,3]cal(*l) #-->cal(1[0],1[1],1[2])```
-
-
按照关键字传参
def cal(a,b,c):pass cal(a = 1,b = 2,c = 3)
-
**字典
def cal(a,b,c):pass d = {'a':1,'b':2,'c':3} cal(**d) #按照关键字传参,字典特有
-
混合:先位置,在关键字
def cal(a,b,c):pass cal(1,2,c = 3)
-
-
站在定义函数的角度上(定义参数要按以下顺序)
-
位置参数
-
*args 动态参数 #默认参数不能再动态参数之前
-
默认参数
-
**kwargs 动态参数
def cal(a,b,*args,c = 10,d = '男',**kwargs) #按照这个顺序调用,中间的默认参数可以设置多个
-
-
参数的陷阱,如果默认参数的值是一个可变数据类型
-
那么所有的调用者都共享这一个数据
-
这个变量是在定义的时候创建1次,而不会在调用的过程中再次被创建了
def func(l = []): #这个列表在定义的时候已经被创建出来,再次调用不会再创建了l.append(1)print(l)func() func() 结果:
[1]
[1,1]def func(a,l = []):
print(id(l))
l.append(a)
print(l)
return lfunc(1) #没传传参数就用默认列表
func(2,[]) #因为传了[]就往[]里传参数
func(3) #没传参数,就用默认列表就放3
func(4,[]) # [4]def func(a,l = []):
print(id(l))
l.append(a)
print(l)
return lfunc(1)
l1 = func(2,[])
func(3)
func(4,[]) # [4] -
2.今日内容
1. 装饰器
-
import time #时间模块
- 被人写好的一些功能,放在一个模块里
- 和时间相关的功能,就放在了time模块
import time print(time.time()) 1564282438.9430697 # 格林威治时间 - 19700101 08:0:00 北京 # 格林威治时间 - 19700101 00:0:00 伦敦 #1564282438.9430697 #1564282488.7851577 #时间模块一般用来计算某个程序所用的时间,效率测试
print('start') time.sleep(1) print('end')
import time def func():sum_num = 0for i in range(100000):sum_num += i print(time.time()) func() print(time.time())改进 import time def func():sum_num = 0for i in range(100000):sum_num += i start = time.time() func() print(time.time()-start)
1.2 函数进阶
-
函数名赋值
def a():print('in a func')a() print(a) b = a print(b) b() # 并不是只有函数名()才能调用一个函数 #只要是函数的地址对应的变量都可以通过()调用函数 l = [a] #a存的是内存地址 print(l[0]) #这个也是指向a的内存地址 l[0]() #所以得出的结果还是in a func # 实际上函数的名字可以被赋值,也可以作为一个容器类型的元素 # 变量怎么使用,函数的名字都可以怎么使用
-
函数的名字做参数
def a():print('in a func') def wahaha(f):print(f)f() wahaha(a)
-
函数的名字做返回值
def wahaha():def a():print('in func a')return a funca = wahaha() #函数a作为wahaha的返回值赋予给了funca funca() #调用funca()就返回了函数a,其中内存地址都指向同一个地方
1.3 闭包
-
闭包:如果一个内部的函数引用了外部函数的变量,那么这个内部函数就是一个闭包函数def wahaha():
name = 'alex'def a():#'''一旦内层函数引用了外层函数的变量,a就是一个闭包函数'''print('in a func',name)return a a = wahaha() a() print(a.__closure__) #a.__closure__不是空元祖,a就是闭包函数,如果没有引用外部变量就打印None
#不是闭包函数,因为inner中没有用到a,b变量 def func():a = 1b = 2def inner()print(1,2)
# inner中虽然用到a,b,但是用到的是自己的参数a,b,而不是外部的变量所以不是闭包 def func():a = 1b = 2def inner(a,b)print(a,b)
def func2():a = 1b = 2def inner(): #是闭包print(a,b)
def func2(a,b):def inner(): #是闭包print(a,b)
a = 1 b = 2 def func2():def inner(): #不是闭包print(a,b)
-
闭包的作用:需要把某一个数据存在局部作用域里,既不被污染又能够时时刻刻能重复使用,不用重复创建的时候,使用闭包
#请求一个网页 from urllib import request # ret = request.urlopen('') # print(ret.read().decode('utf-8')) def get_source_html(url):ret = request.urlopen(url)return ret.read().decode('utf-8') ret = get_source_html('/') print(ret)
#请求一个网页 from urllib import request def get_source_html(url):dic = {}def get_url():if url in dic:return dic[url]else:ret = request.urlopen(url)dic[url]=retreturn ret.read().decode('utf-8')return get_url get_url = get_source_html('/') print(get_url()) print(get_url()) print(get_url())
1.4 装饰器
-
计算函数效率
# import time # def timmer(funcname): # start = time.time() # funcname() # print(time.time()-start) # def func(): # sum_num = 0 # for i in range(100000): # sum_num += i # def func2(): # sum_num = 0 # for i in range(100000): # sum_num += i # # timmer(func) # timmer(func2)
改进一: import time #用到了闭包,函数作为返回值,函数作为参数 def timmer(funcname): #funcname就是'func的内存地址'def inner(*args,**kwargs):start = time.time()funcname(*args,**kwargs)print(time.time()-start)return innerdef func(n,m):sum_num = 0for i in range(n,m):sum_num += iprint('sum_num:',sum_num) def func2():sum_num = 0for i in range(100000):sum_num += iprint('in func2',sum_num)func = timmer(func) #func = inner的内存地址 func(n = 100000000,m = 20) #想调用func,实际调的inner func2 = timmer(func2) func2()
改进二: import time #用到了闭包,函数作为返回值,函数作为参数 def timmer(funcname): #funcname就是'func的内存地址'def inner(*args,**kwargs):start = time.time()ret = funcname(*args,**kwargs)print(time.time()-start)return retreturn innerdef func(n,m):sum_num = 0for i in range(n,m):sum_num += ireturn sum_num def func2():sum_num = 0for i in range(100000):sum_num += ireturn sum_numfunc = timmer(func) #func = inner的内存地址 ret = func(100000,20) #想调用func,实际调的inner func2 = timmer(func2) ret2 = func2() print(ret,ret2)
[外链图片转存失败(img-9H85k0ie-1564886425127)(C:\Users\贡金敏\AppData\Roaming\Typora\typora-user-images\1564640440071.png)]
1.5 什么情况下用,不改变代码
-
@timmer能够让函数快速加上装饰器
import time #用到了闭包,函数作为返回值,函数作为参数 def timmer(funcname): #funcname就是'func的内存地址'def inner(*args,**kwargs):start = time.time()ret = funcname(*args,**kwargs)print(time.time()-start)return retreturn inner@timmer #就是func = timmer(func) #func = inner的内存地址 def func(n,m):sum_num = 0for i in range(n,m):sum_num += ireturn sum_num@timmer #func2 = timmer(func2) #func2 = inner的内存地址 def func2():sum_num = 0for i in range(100000):sum_num += ireturn sum_numret = func(100000,20) #想调用func,实际调的inner ret2 = func2() print(ret,ret2)
-
什么情况下用
-
在已经写好发版的程序的基础上,需要对一个函数执行前后增加功能的时候
- 开放封闭原则
- 开放:对扩展是开放的(增加新功能)
- 封闭:对修改时封闭(可以修改装饰器,但内部函数不要修改)
- 开放封闭原则
-
有的时候也会写好一些装饰器,加在需要装饰的函数上面
-
login Django框架里也这么用
-
log
使用场景 #用户登录 # def login(): # pass # # @login # def lianxi(): # pass # # @login # def gaunli(): # pass # # @login # def xinsuibi(): # pass#论坛程序 # def log(): # pass # # @log # def 发布帖子(): # pass # # @log # def 评论(): # pass
-
-
1.6 装饰器的固定格式
def 装饰器的名字(被装饰的函数名func):def inner(*args,**kwargs): #wahaha参数也传给inner'''在执行被装饰的函数之前要做的事''''''判断是否登录'''ret = func(*args,**kwargs) #inner之后传给func'''在执行被装饰的函数之后要做的事''''''写log'''return ret #调用wahaha返回wahaha的返回值return inner #inner没有括号@装饰器的名字
def wahaha():pass
wahaha() #调用wahaha实际上调用的是inner
[外链图片转存失败(img-UyW3EhYv-1564886425128)(C:\Users\贡金敏\AppData\Roaming\Typora\typora-user-images\1564642148031.png)]
-
函数名.name
def register(name):print('%s注册'%name) print([register.__name__])
-
用户登录
import time def wapper(func):def inner(*args,**kwargs):ret = func(*args,**kwargs)t = time.strftime('%Y-%m-%d %H:%M:%S')print('在%s[%s]执行了%s函数'%(t,args[0],func.__name__))return retreturn inner@wapper def login(name):print('%s登录'%name)@wapper def register(name):print('%s注册'%name) # print([register.__name__])login('alex') register('alex')
-
将用户登录信息记录到operate.log日志文件里
import time def wapper(func):def inner(*args,**kwargs):ret = func(*args,**kwargs)t = time.strftime('%Y-%m-%d %H:%M:%S')with open(r'operate.log',mode='a',encoding='utf-8') as f:f.write('\n在%s[%s]执行了%s函数'%(t,args[0],func.__name__))return retreturn inner@wapper def login(name):print('%s登录'%name)@wapper def register(name):print('%s注册'%name) # print([register.__name__])login('alex') register('alex')
改进 import time from functools import wraps def wapper(func): #告诉装饰器装饰的是func@wraps(func)def inner(*args,**kwargs):ret = func(*args,**kwargs)t = time.strftime('%Y-%m-%d %H:%M:%S')with open(r'operate.log',mode='a',encoding='utf-8') as f:f.write('\n在%s[%s]执行了%s函数'%(t,args[0],func.__name__))return retreturn inner@wapper def login(name):print('%s登录'%name)@wapper def register(name):print('%s注册'%name) print([register.__name__])login('alex') register('alex')
-
函数的注释(独立的函数都要写注释)
def func(a,b):'''函数的注释:这个函数是做什么用的:param a: 表示一个参数 int 长方形的宽:param b: int 长方形的长:return: int 长方形的面积'''return a+b#param 参数 形参# args agrguments 实参print(func.__doc__)
-
1.7 一个函数被多个装饰器装饰
-
装饰器越离函数进越放在里面
def wrapper1(func): #func = inner2def inner(*args,**kwargs):print('before inner1')ret = func(*args,**kwargs)print('after inner1')return retreturn inner def wrapper2(func): # func = wahahadef inner2(*args,**kwargs):print('before inner2')ret = func(*args,**kwargs)print('after inner2')return retreturn inner2 @wrapper1 #wahaha = wrapper1(inner2) = inner1 @wrapper2 #wahaha = wrapper2(wahaha) = inner2 def wahaha():print('WAHAHA')wahaha() #inner1
[外链图片转存失败(img-vd2gKJWR-1564886425130)(C:\Users\贡金敏\AppData\Roaming\Typora\typora-user-images\1564645131061.png)]
-
记录登录时间逻辑
# timmer 装饰器 计算被装饰的函数的执行时间 # login装饰器 检测被装饰的函数是不是登录,如果没有登录要先登录才能使用这个函数 # timmer 装饰器永远紧贴着被装饰的函数 # login 装饰器 没有特殊需求可以任意排列 # logger 装饰器 没有特殊需求可以任意排列@login @timmer #所以timmer贴近函数 def get_html():pass # 先判断登录 #如果没登录#调用登录 input() #输入20s # 如果登录了# get_html --被装饰的函数 # 先记录当前时间 = 登录 + get_html #get_html() #记录结束时间
1.8 带参数的装饰器
-
计算时间
报错,以后修正 ''' /usr/bin/env python -*- conding:utf-8 -*- ----gongjinmin---- '''# 带参数的装饰器 # flask框架中 路由里用到了带参数的装饰器 import time flag = False def timmer(flag): #flag = Truedef outer(func): # func =wahahadef inner(*args,**kwargs):if flag:start = time.time()ret = func(*args,**kwargs)print(time.time()-start)else:ret = func(*args, **kwargs)return retreturn innerreturn outer@timmer(True) #@outer = wahaha = outer(wahaha) def wahaha():print('in wahaha')@timmer def wahaha1(flag):print('in wahaha1')@timmer def wahaha2(flag):print('in wahaha2')@timmer def wahaha3(flag):print('in wahaha3')wahaha() wahaha1() wahaha2() wahaha3()
-
登录改进
''' /usr/bin/env python -*- conding:utf-8 -*- ----gongjinmin---- ''' import time from functools import wraps def log(filename):def wapper(func): #告诉装饰器装饰的是func@wraps(func)def inner(*args,**kwargs):ret = func(*args,**kwargs)t = time.strftime('%Y-%m-%d %H:%M:%S')with open(filename,mode='a',encoding='utf-8') as f:f.write('\n在%s[%s]执行了%s函数'%(t,args[0],func.__name__))return retreturn innerreturn wapper@log('login.log') def login(name):print('%s登录'%name)@log('register.log') def register(name):print('%s注册'%name) print([register.__name__])login('alex') register('alex')
2. 迭代器
-
迭代器
# 需要一个从1-100000000的数字序列 # py2和py3的区别 #py2 range(100000000)真的在内存中生成100000000个数#xrange(100000000) 相当于py3并不是在执行的时候生成100000000个数 #py3 range(100000000) 并不是在执行的时候生成100000000个数
ran = range(100) print(ran) #找ran拿数字了 # ran变成了一个迭代器 iterator = ran.__iter__() #获取迭代器 print(iterator) print(iterator.__next__()) print(iterator.__next__()) print(iterator.__next__()) #迭代器只记当前的数,获取下一个数就加 #迭代器能够从一个数据里面一个一个的取值并且这个数据里取出的所有的值在没取之前不占内存
-
迭代器
- 一个一个的取值,而不是一次性把所有数据都创建出来,迭代器中的数据不取不创建
- 只能按照顺序取,不能跳过也不能回头
- 一个迭代器中的数据只能从头到尾取一次
-
可迭代器:如果一个数据类型中有iter方法,那么这个数据就是可迭代类型
-
迭代器协议:如果一个数据类型中有iter和next方法,那么这个数据类型就是一个迭代器类型
-
可迭代器协议包含迭代器协议
#可迭代的 print(dir()) #查看方法 print('__iter__' in dir('abc')) #字符串里有iter方法 print('__iter__' in dir(123)) #False print('__iter__' in dir(123.43)) #False print('__iter__' in dir({1,2,3})) #True print('__iter__' in dir((1,2,3))) #True print('__iter__' in dir({1:3})) #True f = open(r'G:\py27\其他脚本\register.log',encoding='utf-8') print('__iter__' in dir(f)) #True f.close() print('__iter__' in dir(range(10))) #True
#迭代器 print('__next__' in dir('abc')) #False #对于基础数据类型来说都不是迭代器,只是可迭代类型 f = open(r'G:\py27\其他脚本\register.log',encoding='utf-8') print('__next__' in dir(f)) #True f.close() #文件操作符是一个迭代器
-
迭代器的特点:节省内存空间
-
除了文件操作符以外你见过的所有内容都不是迭代器类型
都只是可迭代类型 -
可迭代类型是可以通过某些方法转换成迭代器的
文件操作符节省空间 f = open(r'G:\py27\其他脚本\register.log',encoding='utf-8') for line in f:print(line) #一行行打印,打印第二行时第一行占用的内存空间会被回收 f.close()
-
-
可迭代类型.__iter__方法可以把可迭代类型转换成迭代器
l = [1,2,3,4,5,6] iter = l.__iter__() print(iter.__next__()) print(iter.__next__()) print(iter.__next__()) print(iter.__next__()) print(iter.__next__()) print(iter.__next__()) print(iter.__next__())
for循环的基本运行原理 l = [1,2,3,4,5,6] num = l.__iter__() while True:try:print(num.__next__())except StopIteration:break
-
所有能被for循环的至少是一个可迭代类型
-
今后如果我说xxx是一个迭代器,知道这个xxx并不是直接存储了内容,而是需要for循环才能获取每一个值
拿衣服: def make_cloth(n):clothes = []for i in range(n):clothes.append('第%s件衣服'%i)return clothes cloth_lst = make_cloth(100) print(cloth_lst)改进:生成器 def make_cloth_simple(n):'''生成器函数,没有return 只有yield:param n::return:'''print('做第一件衣服')yield 1print('做第二件衣服')yield 2print('做第三件衣服')yield 3ret =make_cloth_simple(4) print(ret) #generator 生成器 生成器 生成器的本质就是一个迭代器 # print(ret.__next__()) # print(ret.__next__()) # print(ret.__next__()) for i in ret:print(i)
3. 生成器
-
生成器
-
作用:写生成器函数主要是为了不断的从一个容器里面或一个规则里不断的取需要的一个个数据,既能节省内存又能完成循环或迭代
def make_cloth_simple(n):for i in range(n):yield '第%s件衣服'%i #会记住执行到哪里 ret = make_cloth_simple(10000) print(ret) for n in range(20):print(ret.__next__()) print('='*50) for i in range(50): #接下来在执行会从之前执行的开始print(ret.__next__())
-
示例
#监听文件内的输入 #在python中监听一个文件的输入事件,只要用户输入了新内容,就打印到屏幕中来 with open(r'G:\py27\其他脚本\test',mode='r',encoding='utf-8') as f:while True:ret = f.readline()if ret: #如果ret有值print(ret) #打印 改进版: def listen():with open(r'G:\py27\其他脚本\test',mode='r',encoding='utf-8') as f:while True: #可能执行了2000次循环ret = f.readline().strip()if 'error'in ret: #如果ret有值,可以过滤出error错误日志yield ret #生成器,特点是暂停 for ret in listen(): #而for只有拿到了真实的值才会打印print(ret)改进:程序写入 while True:with open(r'G:\py27\其他脚本\test',mode='a',encoding='utf-8') as f:content = input('>>>')f.write(content)程序读取: def listen():with open(r'G:\py27\其他脚本\test',mode='r',encoding='utf-8') as f:while True: #可能执行了2000次循环ret = f.readline().strip()if 'error' in ret: #如果ret有值yield ret #生成器,特点是暂停 for ret in listen(): #而for只有拿到了真实的值才会打印print(ret)
注意:Linux和Mac不支持手动输入数据再读取,只支持程序输入和程序读取
[外链图片转存失败(img-dmQ6VDl1-1564886425131)(C:\Users\贡金敏\AppData\Roaming\Typora\typora-user-images\1564721892801.png)]
# 将来你写的所有的代码 最好都把读文件的操作写成一个生成器 #代码紧耦合 def login():user = input('username:')pwd = input('password:')with open(r'G:\py27\其他脚本\test',mode='r',encoding='utf-8') as f:for line in f:usr,passwd=line.strip().split('|')if user == usr and pwd == passwd:print('登录成功')breakelse:print('登陆失败') login()
#拆分代码思维逻辑 #登录#读文件 #注册#检测是不是已经存在相同的用户名 一、 def get_user(file):line_lst = []with open(file,mode='r',encoding='utf-8') as f:for line in f:usr,pwd = line.strip().split('|')line_lst.append((usr,pwd))return line_lstdef login():user = input('username:')passwd = input('password:')for usr,pwd in get_user('test'):if usr == user and pwd == passwd:print('登录成功')breakelse:print('登陆失败') login()
改进二: def get_user(file):with open(file,mode='r',encoding='utf-8') as f:for line in f:usr,pwd = line.strip().split('|')yield usr,pwddef login():user = input('username:')passwd = input('password:')for usr,pwd in get_user('test'):if usr == user and pwd == passwd:print('登录成功')breakelse:print('登陆失败') login()
-
注意
- 所有的读文件和写文件 尽量都拆分到其他函数中
- 不要嵌到你写好的功能函数中
- 比如 登录 函数中不要写读文件的逻辑
- 比如 注册 函数中不要写读\写文件的逻辑
-
生成器:特点:一个生成器中的数据也是只能从头到尾取一次
def func():for i in range(10):yield 'a%s'%i g = func() #对同一个生成器迭代 for i in g:print(i) for i in g:print(i) 结果: 生成10个数
def func(): for i in range(10):yield 'a%s'%i for i in func(): #每次生成一个新的生成器迭代print(i) for i in func():print(i) 结果: 生成20个数
def func():for i in range(10):yield 'a%s'%i g1 = g2 =func() #还是同一个生成器 for i in g1:print(i) for i in g2:print(i) 结果: 生成10个数
def func():for i in range(10):yield 'a%s'%i g1 = func() #两个生成器 g2 =func() for i in g1:print(i) for i in g2:print(i) 结果: 生成20个数
3.1 列表推导式
-
示例
l = [1,2,3,4,5,6] ls = [] for i in l :ls.append(i*2) print(ls)改进 l = [1,2,3,4,5,6] ls = [i*2 for i in l] print(ls)
# 所有的偶数都放到新的列表中 l = [1,2,3,4,5,6] ls = [ i for i in l if i%2 == 0] print(ls)
# 30以内所有能被3整除的数 ls = [i for i in range(30) if i%3 == 0] print(ls)
#取 100以内的数 ls = [ i for i in range(100)] print(ls)
# 30以内所有能被3整除的数的平方 ls = [i*i for i in range(30) if i%3 == 0] ls = [i**2 for i in range(30) if i%3 == 0] print(ls)
# 找到嵌套列表中名字含有两个‘e’的所有名字 names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]ls = [name for lst in names for name in lst if name.count('e') == 2 ] print(ls)
3.2 生成器表达式
-
生成器表达式
-
列表推导式的[] 换成() 就变成了生成器表达式了
lst = [i*2 for i in range(5)] print(lst) # lst中的所有元素已经在内存里了gen = (i*2 for i in range(5)) print(gen) # gen里什么也没有##三种取值方式 #gen.__next__() #for n in gen:pass #list(gen)
gen = (i*2 for i in range(5)) print(gen) print('--->',gen.__next__()) for i in gen:print(i)
#报错StopIteration gen = (i*2 for i in range(5)) print(gen) list(gen) #数字在这里被取完了 print('--->',gen.__next__()) for i in gen:print(i)
gen = (i*2 for i in range(5)) print(gen) print('--->',gen.__next__()) print(list(gen)) for i in gen:print(i)
-
-
区别:
- 生成器元素永远需要取才能有不取就没有
- 列表推导式一旦写好元素就已经在了
4. 总结
-
迭代器 是一个可以被for循环的节省内存的类型
-
生成器 是程序员能够自己写的迭代器(生成器本质是迭代器)
- 生成器函数 yield
- g = 生成器函数()
- 生成器表达式
- g = (表达式)
- 列表推导式和生成器表达式所使用的的 “表达式是相同的”
- 生成器函数 yield
-
所有的生成器都符合迭代器的特点
- 1.一个一个的取值,而不是一次性把所有数据都创建出来,迭代器中的数据不取不创建
- 2.只能按照顺序取,不能跳过也不能回头
- 3.一个迭代器中的数据只能从头到尾取一次
-
从迭代器中取值的三种方式
- 1.gen.next
- 2.for n in gen:pass
- 3.list(gen)
-
简单的for循环生成新列表 -->列表推导式联系起来
-
只要用到列表推导式 --> 把列表推导式转成生成器表达式
-
生成器函数 --> 处理文件
-
面试题一 : 生成器函数 + 生成器表达式
- 生成器中的值只能从头到尾取一次
def demo():for i in range(4):yield ig=demo()g1=(i for i in g) g2=(i for i in g1)print(list(g1)) print(list(g2))
[外链图片转存失败(img-MSJD3GHR-1564886425131)(C:\Users\贡金敏\AppData\Roaming\Typora\typora-user-images\1564729498098.png)]
-
面试题二:重点内容 : 生成器 + for循环
- for循环一定要拆
- 生成器代码不取值不调用
def add(n,i):return n+idef test():for i in range(4):yield ig=test() for n in [1,10]:g=(add(n,i) for i in g)print(list(g)) #解析 n=1 n10 g = (add(n,i) for i in add(n,i) for i in test()) g = (add(n,i) for i in add(n,i) for i in (0,1,2,3)) g = (add(n,i) for i in (10,11,12,13)) g = (20,21,22,23)
[外链图片转存失败(img-wvZLd2GF-1564886425132)(C:\Users\贡金敏\AppData\Roaming\Typora\typora-user-images\1564729791765.png)]
5. 匿名函数
5.1 三元运算符
-
示例
a = 10 b = 15 max_num = 0 if a>b:max_num = a else:max_num = b max_num = a if a>b else b print(max_num)
-
匿名函数:
- 如果一个函数中的功能非常小,只有一句代码
- 这个时候我们就可以把这个函数创建成一个匿名函数
- lambda表达式 == 匿名函数 (所有匿名函数用lambda表达)
def func1(a,b):return a+b ret1 = func1(1,2) print(ret1)匿名函数: func2 = lambda a,b:a+b ret2 = func2(3,4) print(ret2)
def func1(a,b):return a+bfunc2 = lambda a,b:a+bprint(func1.__name__) print(func2.__name__) 结果: func1 <lambda>
-
简单到一句话可以描述清楚才用lambda表达式
lambda表达式 a,b两个值,求比较大的值 lambda表达式 a为参数,求a的奇\偶性 lambda表达式 a为参数,求a的绝对值func1 = lambda a,b : a if a>b else b ret = func1(10,2) print(ret)func2 = lambda a : '偶数' if a%2 == 0 else '奇数' ret = func2(11) print(ret)func3 = lambda a:a if a>0 else -a ret = func3(-2) print(ret) ret = func3(6) print(ret)func = lambda :2+3+4 #不传参,比较少见,lambda没有return ret = func() print(ret)
6. 递归函数
g)
print(list(g))
#解析
n=1
n10
g = (add(n,i) for i in add(n,i) for i in test())
g = (add(n,i) for i in add(n,i) for i in (0,1,2,3))
g = (add(n,i) for i in (10,11,12,13))
g = (20,21,22,23)
[外链图片转存中...(img-wvZLd2GF-1564886425132)]### 5. 匿名函数#### 5.1 三元运算符* 示例
a = 10
b = 15
max_num = 0
if a>b:
max_num = a
else:
max_num = b
max_num = a if a>b else b
print(max_num)
* 匿名函数:* 如果一个函数中的功能非常小,只有一句代码
* 这个时候我们就可以把这个函数创建成一个匿名函数
* lambda表达式 == 匿名函数 (所有匿名函数用lambda表达)
def func1(a,b):
return a+b
ret1 = func1(1,2)
print(ret1)
匿名函数:
func2 = lambda a,b:a+b
ret2 = func2(3,4)
print(ret2)
def func1(a,b):
return a+b
func2 = lambda a,b:a+b
print(func1.name)
print(func2.name)
结果:
func1
* 简单到一句话可以描述清楚才用lambda表达式
lambda表达式 a,b两个值,求比较大的值
lambda表达式 a为参数,求a的奇\偶性
lambda表达式 a为参数,求a的绝对值
func1 = lambda a,b : a if a>b else b
ret = func1(10,2)
print(ret)
func2 = lambda a : ‘偶数’ if a%2 == 0 else ‘奇数’
ret = func2(11)
print(ret)
func3 = lambda a:a if a>0 else -a
ret = func3(-2)
print(ret)
ret = func3(6)
print(ret)
func = lambda :2+3+4 #不传参,比较少见,lambda没有return
ret = func()
print(ret)
### 6. 递归函数
发布评论