函数的优势:
1,减少代码的重复性。
2,使代码可读性更好。
自定义函数:
def 关键字 空格 函数名:英文冒号
函数体
执行函数 函数名+()
函数是以功能为导向的。
函数的返回值:
return 作用:1)函数中遇到return 结束函数,下面代码不执行,相当于while循环中的break。
2)将函数里面的值返回给函数的执行者(调用者)。
1)举例 def login(): print(111) print(222) return print(333) login() #函数执行者 2)举例: 函数的初识: def login(): print(111) print(222) return 666 print(login())# 666
return使用方法总结:
第一种情况:只有return 什么都没写 ,或者函数中没有return,函数执行者返回 None
什么是None:python为了节省内存,对于[],{},(),'',set()只要是空的 都保存在None中,是个独立的数据类型。
第二种情况:return None(将None写出来)
第三种情况:return 单个值(这个值是什么类型,返回的值给执行者就是什么类型)
def login(): a = 2 b = 3 return a print(login()) # 2
第四种情况:return 多个值(以元组的形式返回给函数的调用者)
函数的使用:
常用方法计算元素长度:
l1 = [1,2,3,4,1,6,9,10]
print(len(l1))
自定义函数计算方法:
def my_len():
l1 = [1,2,3,4,1,6,9,10]
count = 0
for i in l1:
count +=1
return count
print(my_len())
*************************************************
函数的参数:
举例说明:
def my_len(a): #形式参数 形参
l1 =a
count = 0
for i in l1:
count +=1
return count
l1 = [1,2,3,4,1,6,9,10]
my_len(l1) #实际参数 实参
实参角度:三种
位置参数 :实参和形参数一一对应
举例说明:
def tes(a,b,c):
print(111)
print(a,b,c)
tes(22,'alex',[11,22,33])
关键字参数 :也得一一对应 实参和形参数量相等,实参的顺序可以变(可以理解为分别赋值)
举例说明:
def func(x,y):
print(x,y)
func(y=333,x=444 ) #也可以不用写x= y= 直接写值也行
混合参数(位置参数,关键字参数):整体数量要一一对应,位置参数必须在前面 多个关键字参数位置可以互换,不用一一对应
举例说明:
def func(x,y,z):
print(x,y,z)
func(111,222,z=555)
形参角度:三种
位置参数:相对于实参位置而言 也得一一对应
默认参数: 默认参数必须放在 形参的位置参数后面,不传值就是默认值, 传值则覆盖默认值。
举例说明:
def func2(x,y,z=100):#默认参数
print(x,y,z)
func2(1,2)
def func2(x,y,z=100):
print(x,y,z)
func2(1,2,3000) #为z赋值
动态参数:用户传入到函数中的实参数量不定时,或为以后拓展,此时要用到动态参数。
此时要用到动态参数*args,**kwargs(这个英文可以改,他们只是个变量,也叫万能参数)
*args接受的是所有的位置参数。形成元组
**kwargs接受的是所有的关键字参数。形成字典
举例说明:
def func1(*args,**kwargs):
print(args)
print(kwargs)
func1(1,2,3,4)
func1(x=4,y=5,z=6)
func1(1,2,3,x=4,y=5,z=6)
动态参数的位置:
*args:
位置参数 *args 默认参数
**kwargs:
放在最后面:位置参数 *args 默认参数,**kwargs.
举例说明:
def func1(a,b,*args,sex='男',**kwargs): print(a) print(b) print(args) print(sex) print(kwargs) func1(1,2, 5,6,7,8,9,sex='女',x=6,y=5,name='alex')
补充:
#如果函数的默认参数是可变的数据类型,无论使用多少次默认参数,都是一个,并且如果默认参数是{} [] ()等 实参位置不用加索引(这里和学习深浅copy有相似地方)
def extendlist(val,list=[]):
list.append(val)
return list
list1=extendlist(10)
list2=extendlist(123,[])
list3=extendlist('a')
list=[10,'a'] #默认列表
list=[123,] #新列表
list=[10,'a'] #一变都变,默认列表
print('list1=&s' %list1)
*的魔性用法:将可迭代对象 直接将每个元素加入到args中
在函数的定义的时候,*代表聚合和聚合
举例说明:
def func3(*args,**kwargs):#函数的定义,*用意是聚合 print(args) l1=[1,2,3] l2=[11,21,32] func3(*l1,*l2)# *的用意是打散 def func3(*args,**kwargs): print(args) print(kwargs) dic1 = {'name':'alex'} dic2 = {'age':1000} func3(**dic1,**dic2) # 是两个*
补充:
def func(*args,**kwargs):在这是聚合
print(args) # (1,2,3)
print(*args) # 1 2 3 在这又打散
func1(*[1,2,3],**{'name':'alex'}) 在这* 是打散
def func(*args,**kwargs):
print(*kwargs) # (1,2,3)#print函数无法接受**kwargs关键字参数->print('name'='alex')
print(kwargs) #但是你加* 会输出字典的键。
func(*[1,2,3],**{'name':'alex','ags':18})
*处理剩下的元素
*除了在函数中可以这样打散,聚合外,函数外还可以灵活的运用:
# 之前讲过的分别赋值 a,b = (1,2) print(a, b) # 1 2 # 其实还可以这么用: a,*b = (1, 2, 3, 4,) print(a, b) # 1 [2, 3, 4]
名称空间,加载顺序等
当程序运行时,代码从上至下以此读取,遇到变量与值,它会在内存中开辟一个空间,存放变量与值得内存地址的对应关系。
这样存储变量与值得对应关系的空间叫做名称空间(命名空间)又称全局名称空间
当解释器遇到函数时,它会将函数名存储在内存中,但是对于函数体漠不关心。
当函数执行时,它会通过函数名找到函数体,然后将函数体里面的变量等对应关系存放在一个临时开辟的空间中,随着函数的结束,临时的空间关闭,这样的空间叫做临时名称空间。
另一点:函数中的变量只能在函数内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。
内置名称空间:存放了python解释器为我们提供的名字:input,print等
如len() print() input()
名称空间:
内置名称空间
全局名称空间
局部名称空间
加载顺序:
内置名称空间 --> 全局名称空间(程序运行时)--> 临时(局部)名称空间(函数调用时执)
作用域:
全局作用域:内置名称空间,全局名称空间
局部作用域:局部名称空间
取值顺序(只能单项):临时(局部)名称空间 --> 全局名称空间 --> 内置名称空间
l local
E eclose
G global
B built-in
举例说明:
sum = 666 #global def func1(): sum = 555 #eclose print(sum) def inner(): sum = 111 #local print(sum) inner() func1()
检测那些变量是全局的那些事局部的,globals() locals()
#locals():函数会以字典的类型返回当前位置的全部局部变量 (可以检查局部,也可以检查全局)。
#globals():函数以字典的类型返回全部全局变量。
def extendList(val,list=[]): list.append(val) print(locals()) return list #这个list是局部的 ret = extendList(1) #但是list返回的值是全局的 print(globals())
函数的嵌套:主要是要明白数据的执行顺序。
举例说明: def func1(): a =666 print(666) def func2(): name = 'adf' print(name) func1() print(333) func2() print(555)
**********************global nonlocal 关键字*******************
在全局名称空间里得到局部名称空间的变量用:
1, return
举例说明:
def func1(): name='alex' print(name) return name ret = func1() print(ret)
2,global :1)在局部作用域中声明一个全局变量。
def func1(): global name name='alex' func1() print(name)
2)在局部作用域中更改一个全局变量(在局部作用域想要对全局作用域的全局变量进行修改时,需要用到 global(限于字符串,数字))
name = '小旋风' def func1(): global name name='男神' func1() print(name)
补充:局部作用域对全局作用域的变量(此变量只能是不可变的数据类型)只能进行引用,而不能进行改变,对于可变的数据类型,函数中如果对其进行操作,改变全局变量不用引入global
l1 =[1,2,3] def func1(): l1.append(666) func1() print(l1)
**nonlocal如果上一层没有变量 就会报错(在局部作用域如果想对父级作用域的变量进行改变时,需要用到nonlocal)
1,不能改变一个全局变量,但是可以改变局部变量
在局部作用域汇中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改
并且引用的哪层,从那层及以下此变量全部发生改变(先是引用,后是修改),这里的哪层指的是同层及同层以下嵌套的。
举例说明:
def func1(): name = '男神' print(name) def inner(): nonlocal name name = '小旋风' print(name) inner() print(name) func1() def add_b(): b =42 def do_global(): b =10 print(b) #10 def dd_nonlocal(): #nonlocal b #10 b = b + 20 #30 print(b) # 30 dd_nonlocal() print(b) # 30 do_global() print(b)# 42 add_b()
补充:
count = 1 def func4(): count = count + 1 #这样会报错 print(count) func4() #局部只能引用全局的变量,不能修改,如果想修改,加global。 count = 1 def func4(): global count count = count + 1 #这样就不会报错 print(count) func4() #子函数只能引用父函数的变量,不能修改,如果想修改,加nonlocal。 def func4(): count = 3 def inner(): nonlocal count #加个nonlocal之后就不会再报错,否则会报错。 count = count + 1 print(count) inner() func4()
*************************函数名的应用***********************
什么是函数名:
1,他是一个变量 直接打印函数名得到的是函数的内存地址,加个()就执行。函数名其实可以认为是特殊的变量,学名:第一类对象。
2,函数名能进行赋值运算。
def func1(): print(666) f1 =func1 f1()
3,函数名可以作为函数的参数
def func1(): print(666) def func2(x): x() print(555) func2(func1)
4,函数名可以作为容器数据类型的元素。
def func1(): print(666) def func2(): print(222) def func3(): print(111) def func4(): print(777) l1 = [func1,func2,func3,func4] for i in l1: i() # 一一全部执行
5,函数名可以作为函数的返回值
def func1(): print(666) def func2(x): print(222) return x ret = func2(func1)
*****************************闭包*************************
补充理解(之前都没有理解闭包是干什么的):
由于闭包这个概念比较难以理解,尤其是初学者来说,相对难以掌握,所以我们通过示例去理解学习闭包。
给大家提个需求,然后用函数去实现:完成一个计算不断增加的系列值的平均值的需求。
例如:整个历史中的某个商品的平均收盘价。什么叫平局收盘价呢?就是从这个商品一出现开始,每天记录当天价格,然后计算他的平均值:平均值要考虑直至目前为止所有的价格。
比如大众推出了一款新车:小白轿车。
第一天价格为:100000元,平均收盘价:100000元
第二天价格为:110000元,平均收盘价:(100000 + 110000)/2 元
第三天价格为:120000元,平均收盘价:(100000 + 110000 + 120000)/3 元
........
series = [] def make_averager(new_value): series.append(new_value) total = sum(series) return total / len(series) print(make_averager(100000)) print(make_averager(110000)) print(make_averager(120000))
从上面的例子可以看出,基本上完成了我们的要求,但是这个代码相对来说是不安全的,因为你的这个series列表是一个全局变量,只要是全局作用域的任何地方,都可能对这个列表进行改变。
series = [] def make_averager(new_value): series.append(new_value) total = sum(series) return total / len(series) print(make_averager(100000)) print(make_averager(110000)) series.append(666) # 如果对数据进行相应改变,那么你的平均收盘价就会出现很大的问题。 print(make_averager(120000))
那么怎么办呢?有人说,你把他放在函数中不就行了,这样不就是局部变量了么?数据不就相对安全了么?
def make_averager(new_value): series = [] series.append(new_value) total = sum(series) return total / len(series) print(make_averager(100000)) # 100000.0 print(make_averager(110000)) # 110000.0 print(make_averager(120000)) # 120000.0
这样计算的结果是不正确的,那是因为执行函数,会开启一个临时的名称空间,随着函数的结束而消失,所以你每次执行函数的时候,都是重新创建这个列表,那么这怎么做呢?这种情况下,就需要用到我们讲的闭包了,我们用闭包的思想改一下这个代码。
def make_averager(): series = [] def averager(new_value): series.append(new_value) total = sum(series) return total/len(series) return averager avg = make_averager() print(avg(100000)) print(avg(110000)) print(avg(120000))
大家仔细看一下这个代码,我是在函数中嵌套了一个函数。那么avg 这个变量接收的实际是averager函数名,也就是其对应的内存地址,我执行了三次avg 也就是执行了三次averager这个函数。那么此时你们有什么问题?
肯定有学生就会问,那么我的make_averager这个函数只是执行了一次,为什么series这个列表没有消失?反而还可以被调用三次呢?这个就是最关键的地方,也是闭包的精华所在。我给大家说一下这个原理,以图为证:
上面被红色方框框起来的区域就是闭包,被蓝色圈起来的那个变量应该是make_averager()函数的局部变量,它应该是随着make_averager()函数的执行结束之后而消失。但是他没有,是因为此区域形成了闭包,series变量就变成了一个叫自由变量的东西,averager函数的作用域会延伸到包含自由变量series的绑定。也就是说,每次我调用avg对应的averager函数 时,都可以引用到这个自用变量series,这个就是闭包。
内层函数对外层函数非全局变量的引用就叫闭包
判断是不是闭包,是函数名.__closure__返回的是None则不是闭包,返回cell则是。
def func1(): name = '小旋风' def inner(): print(name) inner() print(inner.__closure__)判断是否是闭包,如果返回cell 是 None否 func1() 另一例子:比较又迷惑性,这个也是闭包 相当于 在函数中name重新赋值 def func1(x): def inner(): print(x) inner() print(inner.__closure__) name = '小旋风' func1(name)
如果此函数拥有自由变量,那么就可以侧面证明其是否是闭包函数了
def make_averager(): series = [] def averager(new_value): series.append(new_value) total = sum(series) return total/len(series) return averager avg = make_averager() # 函数名.__code__.co_freevars 查看函数的自由变量 print(avg.__code__.co_freevars) # ('series',) 当然还有一些参数,仅供了解: # 函数名.__code__.co_freevars 查看函数的自由变量 print(avg.__code__.co_freevars) # ('series',) # 函数名.__code__.co_varnames 查看函数的局部变量 print(avg.__code__.co_varnames) # ('new_value', 'total') # 函数名.__closure__ 获取具体的自由变量对象,也就是cell对象。 # (<cell at 0x0000020070CB7618: int object at 0x000000005CA08090>,) # cell_contents 自由变量具体的值 print(avg.__closure__[0].cell_contents) # []
闭包有什么用?
当执行一个函数时,如果解释器判断此函数闭包存在,这样函数就有一个机制,闭包的所在的临时名称空间不会随着函数的执行完毕而消失。
闭包的作用:保存局部信息不被销毁,保证数据的安全性。
闭包的应用:
-
可以保存一些非全局变量但是不易被销毁、改变的数据。
-
装饰器。
比较有意思的一道题:
def func(): def func1(): name = '小旋风' def func2(): nonlocal name name = '男神' def func3(): global name name = '女神' name = '小屁孩' func1() print(name) func2() print(name) func3() print(name) func() print(name)
******************************装饰器********************************
装饰器的理解:以前你有没有这样一段经历:很久之前你写过一个函数,现在你突然有了个想法就是你想看看,以前那个函数在你数据集上的运行时间是多少,这时候你可以修改之前代码为它加上计时的功能,但是这样的话是不是还要大体读读你之前的这个的代码,稍微搞清楚一点它的逻辑,才敢给它添加新的东西。这样是不是很繁琐,要是你之前写的代码足够乱足够长,再去读它是不是很抓狂...。实际工作中,我们常常会遇到这样的场景,可能你的需求还不只是这么简单。那么有没有一种可以不对源码做任何修改,并且可以很好的实现你所有需求的手段呢?答案当然是有,这就是今天我们要介绍的python装饰器。有了装饰器,你除了不用担心前面提到的问题,并且还可以很好的处理接下来要做的事:那就是现在你又有了一个新的需求,比如为另一个函数添加计时功能,这时就非常简单了,把要装饰的函数丢给装饰器就好了,它会自动给你添加完功能并返回给你。是不是很神奇?下面我们将一层层剥开它的神秘面纱。
装饰器定义及作用:装饰器本质还是一个函数,它是用来对函数函数进行装饰的函数,在不改变原来函数调用的基础上给原来的函数添加新的功能
装饰器的原则:开放封闭原则
开放:对于原来的函数没有的功能(要添加的新的功能是开放的)
为什么要对扩展开放呢?
任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。
封闭:对于原来的函数已经实现的功能是封闭的(不能改变原来的函数)
为什么要对修改封闭呢?
就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对已经写好的函数进行了修改,很有可能影响其他已经在使用该函数的用户。
装饰器完美的遵循了这个开放封闭原则
import time
可以使用 time.time()计算时间 time.sleep()控制时间及停留多少时间后再执行。
简单装饰器
import time def login(): time.sleep(1) print('同一个世界....') def timmer(f): def inner(): start_time = time.time() f() end_time = time.time() print('此函数的执行时间%s' % (end_time-start_time)) return inner login = timmer(login) login() import time def timmer(f): #装饰器 def inner(): start_time = time.time() f() end_time = time.time() print('此函数的执行时间%s' % (end_time-start_time)) return inner @timmer #login = timmer(login) 加一个‘@’键 def login(): time.sleep(1) print('同一个世界....') login()
函数带返回值的装饰器(万能装饰器)
import time def timmer(f): f = login #具有通用性 def inner(*args,**kwargs): #函数‘*’聚合 #args(2,3) start_time = time.time() ret=f(*args,**kwargs) #实参 执行 ‘*’打散 login() *(2,3) 2,3 end_time = time.time() print('此函数的执行时间%s' % (end_time-start_time)) return ret return inner @timmer #login=timmer(login) inner 此login是新变量 def login(a,b): time.sleep(1) print('同一个世界....') return 666 login(2,3) #inner(2,3)
万能装饰器雏形:
def wrapper(f): def inner(*args,**kwargs): ''执行被装饰函数之前的操作'''' ret = f(*args,**kwargs) ''执行被装饰函数之前的操作'''' return ret return inner @wrapper f()
*******************函数的有用信息**************************
#这个引入模块的作用就是在函数外面通过(函数名.__name__和函数名.__doc__)打印出被装饰函数login内部的注释信息以及函数名,不用深入理解 知道怎么用就行
from functools import wraps #引入模块 def wrapper(f): @wraps(f) #在这边加入 def inner(*args,**kwargs): '''执行前''' ret = f(*args,**kwargs) '''执行后''' return ret return inner @wrapper def login(username,password): ''' 此函数需要用户名,密码两个参数,完成的是登录的功能 ''' print('登录成功...') return True login(1,2) print(login.__name__) #打印函数名 print(login.__doc__) #打印函数里的注释内容
********************带参数的装饰器****************************
#通过立flag=True False 来决定控制函数是否需要被装饰 import time flag = True def timmer(f): def inner(*args,**kwargs): if flag: start_time = time.time() time.sleep(0.3) ret = f(*args,**kwargs) end_time = time.time() print('执行效率%s'%(end_time-start_time)) return ret else: ret = f(*args,**kwargs) return ret return inner @timmer def func1(): print(111) @timmer def func2(): print(222) @timmer def func3(): print(333) func1() func2() func3()
这个就是带参数的装饰器,其作用就是能够很好的控制被装饰器的使用功能,就是在装饰器的原来基础上 在加个函数。
import time def time_out(flag1):#带参数的装饰器 #flag1= flag def timer(f) def inner(*args,**kwargs): if flag: star_time = time.time() time.sleep(2) ret = f(*args,**kwargs) end_time = time.time() print('执行时间%d' %(end_time-star_time)) return ret else: ret = f(*args,**kwargs) return ret return inner return timer flag =True @time_out(flag) #1,先执行timer_out(flag)传入flag参数,然后返回一个timer ;2,@与timer结合,形成熟悉的装饰器@timer def func1(): print(111) @time_out(flag) def func2(): print(222) @time_out(flag) def func3(): print(333) func1() func2() func3()
***********************多个装饰器装饰多个函数******************
def wrapper1(func): #func = f :f是函数名 def inner1(): print('wrapper1,before func')#2 func() print('wrapper1,after func')#4 return inner1 def wrapper2(func): #func=inner1 def inner2(): print('wrapper2,before func') #1 func() #执行inner1() print('wrapper2,after func')#5 return inner2 @wrapper2 #f = wrapper2(f) 里面的f新变量=inner1 外面的函数是最新变量=inner2 @wrapper1 #f=wrapper1(f) 里面的f函数名,外面的f新变量=inner 这个执行结束之后后,开始执行第二个装饰器
(两个装饰器,先执行距离最近的装饰器 在执行其他的 也就是由进到远) def f(): print('in f')#3 f() #inner2()
'''执行结果
wrapper2,before func
wrapper1,before func
in f
wrapper1,after func
wrapper2,after func
'''
补充知识点:
装饰器的本质是闭包
开放封闭原则(写代码最好不要写死):允许代码扩展,添加新功能,如果函数交给其他人使用,所以进行封闭
#局部作用域可以对全局作用域的变量进行引用,不能修改,如果要修改加上global:举例说明 count = 1 def func1():
count =5
print(count) func1() #函数内部如果有变量名与全局变量名相同,且对此变量进行改变,python就会将你引用的变量视为局部定义的变量,但是局部没有定义,那么他会报错。 count = 1 def func1(): count = count + 1 #会报错 print(count) func1() #这种情况也会报错,要理解,都是一个道理 因为func1()会形成一个名称空间 在inner()函数也会形成一个名称空间,一个父级一个子级,这样只能引用不能改变,要改变加nonlocal def func1(): count = 3 def inner():
nonlocal count count=count+1 inner() print(count) func1() 在函数中使用global 要放在函数体的第一个位置 flag = True def func1(): #会报错 if flag: print(333) global flag flag=False func1() flag = True def func1(): #这样就不会报错 global flag if flag: print(333) flag False func1()#for if while 没有开辟临时空间,只有函数时才开启临时空间,只能对外面的数据进行引用,不能更改。 flag = True while flag: print(333) flag = False #这里就是全局变量 所以没有报错 原因是上面那段解释 它没有开辟临时空间 print(222) 函数中如果使用global加一个变量,必须是第一次使用这个变量,也就是在前面。
***************************迭代器**************************
可迭代对象定义:内部含有__iter__方法的就是可迭代对象,遵循可迭代协议。
用dir函数可以检测:可以通过dir() 去判断一个对象具有什么方法,dir()会返回一个列表,这个列表中含有该对象的以字符串的形式所有方法名
print(dir('123'))#返回的是列表
print('__iter__' in dir([1,2,3])) 用这个方法判断列表中是否存在__iter__
dir():查看对象的所有方法
可迭代对象的优点:
可以直观的查看里面的数据。
可迭代对象的缺点:
1. 占用内存。
2. 可迭代对象不能迭代取值(除去索引,key以外)。
那么这个缺点有人就提出质疑了,即使抛去索引,key以外,这些我可以通过for循环进行取值呀!对,他们都可以通过for循环进行取值,其实for循环在底层做了一个小小的转化,就是先将可迭代对象转化成迭代器,然后在进行取值的。那么接下来,我们就看看迭代器是个什么鬼。
迭代器:可迭代对象通过.__iter__()可以转化成迭代器,遵循迭代器协议,其实它也是数据类型。在python中,内部含有'__Iter__'方法并且含有'__next__'方法的对象就是迭代器。 迭代器也是一种数据类型:1)内存占用非常小,节省内存;2)满足惰性机制,在循环到第四次时,再次循环它会从第5次继续执行;迭代器是未来做数据类型;3)取值过程单项不可逆的首选方式
l = [1,2,3] l_obj = l.__iter__() #生成迭代器 l_obj=iter(l) print(l_obj) print(l_obj.__next__())
迭代器取值方法:
1).__next__() :使用一次就取一次值,值取完后会报错。
2) for循环:for循环其实就是将可迭代对象,转化成迭代器,通过.__next__()来实现元素读取。
需要注意一点:
l = [1,2,3] l_obj = l.__iter__() print(l_obj.__next__())#在这里执行.__next__()后再 执行for循环 执行的是剩余的内容,符合迭代器的惰性机制。 print() for i in l_obj: print(i) #只能打印出2 3
生成迭代器以及取值的另一种方法:
l1 = [1, 2, 3, 4, 5, 6] obj = iter(l1) print(next(obj)) print(next(obj)) print(next(obj))
怎样判断是否是迭代器:
1)内部还有.__iter__()和.__next__()方法的就是迭代器。
2) from collections import iterable
from collections import iterator
print(isinstance('123',iterable))#判断是否是可迭代对象 isinstance可以判断所有属性,可以判断任意数据类型。
print(isinstance('123',iterator))#判断是否是迭代器
实际上可迭代对象是不可以一个一个的一次取值的,因为他没有__next__方法,但是
for循环提供一个机制:
1)将可迭代对象转化成迭代器
2)利用__next__进行取值。
3)用try异常处理方法防治报错。
如下例子:模拟for循环机制
l = [1,2,3,4,5] l_obj =l.__iter__() while True: try: print(l_obj.__next__()) except StopIteration: break
迭代器:是一个非常节省内存,可以记录取值位置,可以直接通过循环+next方法取值,但是不直观,操作方法比较单一的数据集。
应用:当你的数据量过大,大到足以撑爆你的内存或者你以节省内存为首选因素时,将数据集设置为迭代器是一个不错的选择。(可参考为什么python把文件句柄设置成迭代器)
*************************生成器******************************
生成器的本质也是迭代器,迭代器都是Python给你提供的已经写好的工具或者通过数据转化得来的,(比如文件句柄,iter([1,2,3])。生成器是需要我们自己用python代码构建的工具。最大的区别也就如此了。
两种构建方法:
1,通过生成器函数构建。
2,通过生成器推导式构建。
1)函数构建生成器:将函数中的return换成yield,这样func就不是函数了,而是一个生成器函数
def func1(): #函数体就是生成器函数 print(666) yield 222 #yield的值是可以返回给生成器的 yield 777 g_obj = func1() #生成器对象 在下面通过func1().__next__()执行这个函数 print(g_obj.__next__()) #生成器也是.__next__()取值 一个yield对应一个.__next__() ,next数超过yield的数就会报错。 print(g_obj.__next__()) 生成器作用的说明举例: def cloth2(): for i in range(1,10001): yield '衣服%s' %i #注意这里的yield不像Reture位置那么死板,比较灵活 g = cloth2() for i in range(1,51): print(g.__next__()) for i in range(1,151): print(g.__next__()) #或者print(next(g))
send的用法
1)send 具有next功能,一次只能执行一个yield
2)send 可以给上一个yield传值 传什么值都行
3)第一个取值不能使用send 但可以是send(None)
def func1(): count = yield #可以通过这个方式打印出yield被更改的值 print(count) yield 777 yield 888 g_obj = func1() print(g_obj.__next__()) print(g_obj.send(2))
def gen(name):
print(f'{name} ready to eat')
while 1:
food = yield 222
print(f'{name} start to eat {food}')
dog = gen('arry')
next(dog) # 第一次必须用next让指针停留在第一个yield后面,这个next其实是执行到yield 222 但是yield是返回值 要看到这个返回值 需要print(next(dog))
# 与next一样,可以获取到yield的值
ret = dog.send('骨头')
print(ret)
yield from的用法
# 对比yield 与 yield from def func(): lst = ['A','B','C','D'] yield lst g = func() print(g) print(next(g)) # 只是返回一个列表 def func(): lst = ['A','B','C','D'] yield from lst g = func() print(g) # 他会将这个可迭代对象(列表)的每个元素当成迭代器的每个结果进行返回。 print(next(g)) print(next(g)) print(next(g)) print(next(g)) ''' yield from ['A','B','C','D'] 等同于: yield 'A' yield 'B' yield 'C' yield 'D' '''
需要注意yield from 是将列表中的每一个元素返回,所以 如果写两个yield from 并不会产生交替的效果
def func(): lst1 = ['A','B,'C','D'] lst2 = ['E','F','G','k'] yield from lst1 yield from lst2 g = func() for i in g: print(i)
2)推导器构建
列表推导式:能用列表推导式完成的,用python代码都可以完成
什么是列表推导式:就是用一句话构建一个你想要的列表
优点,简单,
缺点,不能用debug
列表推导式:
[变量(加工后的变量) for 变量 in 可迭代对象]-----遍历模式
li = [i for i in range(1,12)]
print(li)
[变量(加工后的变量) for 变量 in 可迭代对象 if 判断]-----筛选模式
li = [i for i in range(101) if i%2==0]
print(li)
生成器表达式:
g= (i*i for i in range(1,11)) 只是将[]改为()就能成为生成器对象
print(g)
列表推导式 简单明了 但是占内存 还有字典 集合推导式(需求不多,先不学)。
生成器表达式 节省内存 但是不易看出
#求出下面列表中的两个小列表中含有两个‘e’的元素 names = [['tom','billy','jefferson','andrew','wesley','steven','joe'], ['alice','jill','ana','wendy','jennifer','sherry','eva']] for name in names: for i in name: if i.count('e') == 2: print(i) #使用列表推导式,一般for循环最多循环两层 li = [i for name in names for i in name if i.count('e') == 2] print(li)
字典推导式:这里只是简单介绍一个
#将下面的字典键与值互换位置 mcase = {'a':10,'b':34} mcase_frequency = {mcase[k]: k for k in mcase} print(mcase_frequency)
*****************************内置函数******************************
目前python里共有68个内置函数
黄色一带而过:all() any() bytes() callable() chr() complex() divmod() eval() exec() format() frozenset() globals() hash() help() id() input() int() iter() locals() next() oct() ord() pow() repr() round()
红色重点讲解:abs() enumerate() filter() map() max() min() open() range() print() len() list() dict() str() float() reversed() set() sorted() sum() tuple() type() zip() dir()
蓝色未来会讲: classmethod() delattr() getattr() hasattr() issubclass() isinstance() object() property() setattr() staticmethod() super()
range()自定制一个数字范围列表
frozenset()冻结:语法:frozenset([iterable]),返回一个冻结的集合,冻结后集合不能再添加或删除任何元素。
a = frozenset(range(10)) print(a) #frozenset([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
sum()
isinstance()
next()
enumerate()
作用域相关 globals() 以字典形式返回全部 全局变量
locals() 以字典形式返回当前位置全部 局部变量
def extendList(val,list=[]): list.append(val) print(locals()) #打印的是{'val':1,'list':[1]} return list ret = extendList(1) print(globals())#{'extendList':<function extendList at 03456df>,'ret':1}
1) def extendList(val,list=[]): list.append(val) return list ret = extendList(1) print(globals()) print(locals()) #如果在这里 locals所在的位置是在全局空间里,所以和globals打印的结果一样 2) def extendList(val,list=[]): list.append(val) print(globals()) print(locals()) return list ret = extendList(1) print(globals()) #这里两个globals打印的是有差异的 它打印的应该是当前位置及之前的全局变量 3) def extendList(val,list=[]): list.append(val) print(locals()) def inner(): a = 3 b = 4 print(locals()) #locals只打印出当前的 inner() return list ret = extendList(1)
eval:去除字符串的引号,返回对应内部的值。
s = '{"a":1,"b":3}'
dic = eval(s)
print(dic,type(dic))
exec:去除字符串的引号,没有返回值,但是能执行里面的结果
ret = ''' name = input('请输入名字:').strip() if name == 'xiaoxuanfeng': print(666) ''' exec(ret)
print函数:
print(1,2,3,sep='*')#改变设定打印元素的连接方式
print('我是',end="")#end控制换行
print('xiaoxuanfeng')
f1 = open('log',encoding='utf-8',mode='w')
print('666',file=f1)#操作文件句柄,写入文件。
f1.close()
help:函数用于查看函数或模块用途的详细说明。
内存相关: hash id
hash:将不可变的数据通过hash算法转化成hash值
print(hash('name'))
print(hash(1))输出的还是1
id:打印内存地址
调用相关:
callable:函数用意检查一个对象是否是可调用的(加个()就能执行就是可调用),如果返回True,object仍然可能调用失败
迭代器生成器相关
range iter() next()
数据类型相关的
float:函数用于将整数和字符串转换成浮点数。
complex(复数):
bin:将十进制转换成二进制并返回。
oct:将十进制转换成八进制字符串并返回。
hex:将十进制转换成十六进制字符串并返回。
数学运算:
abs:函数返回数字的绝对值。
divmod:计算除数与被除数的结果,返回一个包含商和余数的元组
print(divmod(7,3))
round:保留浮点数的小数位,默认保留整数。
pow:求x**y次幂 (三个参数为x**y的结果对z取余)
print(pow(2,3)) # 两个参数为2**3次幂 print(pow(2,3,3)) # 三个参数为2**3次幂,对3取余。
sum:对可迭代对象进行求和计算(可设置初始值)
print(sum([1,2,3,4]))
print(sum([1,2,3,4],100))
min:返回可迭代对象的最小值(可加key,key为函数名,通过函数的规则,返回最小值)
print(min([1,3,5,7,-4]))
print(min([1,3,5,7,-4],key=abs))
max:返回可迭代对象的最小值(可加key,key为函数名,通过函数的规则,返回最大值)
list:将可迭代对象创建列表
tu = (1,2,3)
print(list(tu)) 这个其实是创建 也可理解为转换
l1 = list((1,2,3))
print(l1)
相关内置函数:
sorted() 用于可迭代对象,返回的是个列表:
li = [{'sales_volumn':0},{'sales_volumn':108},{'sales_volumn':337},
{'sales_volumn':475},{'sales_volumn':396},{'sales_volumn':172},
{'sales_volumn':9},{'sales_volumn':58},{'sales_volumn':272},
{'sales_volumn':456},{'sales_volumn':440},{'sales_volumn':239},
]
将上面的列表 按照字典中的值排序
print(sorted(li,key=lambda x:x['sales_volumn']))
和函数组合使用 # 定义一个列表,然后根据一元素的长度排序 lst = ['天龙八部','西游记','红楼梦','三国演义'] # 计算字符串的长度 def func(s): return len(s) print(sorted(lst,key=func)) # 结果: # ['西游记', '红楼梦', '天龙八部', '三国演义']
和lambda组合使用 lst = ['天龙八部','西游记','红楼梦','三国演义'] print(sorted(lst,key=lambda s:len(s))) 结果: ['西游记', '红楼梦', '天龙八部', '三国演义']
reversed:将一个序列翻转,并返回此翻转序列的迭代器
l1 = [11,22,33,44,77,66]
l_obj=reversed(l1)
for i in l_obj:
print(i)
slice:构造一个切片对象,用于列表的切片
l1 = [11,22,33,44,77,66]
l2 = [111,222,333,444,775,666]
print(l1[1::2])
rule = slice(1,len(l2),2)
print(l2[rule])
以什么方式切l1就以什么方式切l2
format:与具体数据相关,用于计算各种小数,精算等。
print(format('test','<20')) #左对齐
print(format('test','>20')) #右对齐
print(format('test','^20')) #居中
bytes:str-->bytes
s1='asad'
b1=s1.encode('utf-8')
print(b1)
b2 = bytes(s1,encoding='utf-8')
print(b2)
#通过构造函数创建空 bytes
b1 = bytes()
#通过空字符串创建空 bytes
b2 = b''
bytearray
ret = bytearray('abc',encoding='utf-8')#类比:[97,133,......]
print(id(ret))
print(ret) #bytearray(b'abc')
print(ret[0]) #得到'a'在accii的位置。
ret[0]=65
print(ret)
memoryview:
#分字节去转化成str
b1 = bytes('您好',encoding='utf-8')
print(b1)
ret = memoryview(bytes('您好',encoding='utf-8'))
print(len(ret))
print(ret)
print(bytes(ret[:3]).decode('utf-8'))
print(bytes(ret[3:]).decode('utf-8'))
ord:输入字节找该字节编码的位置。
print(ord('a'))
print(ord('中')) #unicode编码
chr:输入位置数字找出其对应的字符
print(chr(65))
print(chr(20013))
ascii:是ascii码中的返回值,不是就返回/u
print(ascii('a'))
print(ascii(1))
print(ascii('中'))
repr:返回一个对象的string形式(原形毕露)
msg = '名字%r' %('xiaoxuanfeng')
print(msg) #传的是字符串 那么返回的就是字符串
print('{"name":"abc"}')
print(repr('{"name":"abc"}'))
相关内置函数
sorted:对所有可迭代的对象进行排序操作 ,返回的是列表 sorted(iterable,key=None,reverse=False)
print(sorted([1,2,3,4,5,-6]))
print(sorted([1,2,3,4,5,-6],key=abs)) 可以加参数
l = [('a',1),('c',2),('d',4),('b',3),]
print(sorted(l))
def func1(x):
return x[1]
l = [('a',1),('c',2),('d',4),('b',3),]
print(sorted(l,key=func1))
students = [('john','A',15),('jane','B',12),('dave','B',10)] #按照年龄进行排序 print(sorted(students,key=lambda x :x[2])
all:可迭代对象中,全都是True才是True
all() 函数用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False。
元素除了是 0、空、None、False 外都算 True(这里的空指的是可迭代对象里面的元素 如果是空列表,元组,返回的True)。 bool值不是可迭代对象
>>> all(['a', 'b', 'c', 'd']) # 列表list,元素都不为空或0 True >>> all(['a', 'b', '', 'd']) # 列表list,存在一个为空的元素 False >>> all([0, 1,2, 3]) # 列表list,存在一个为0的元素 False >>> all(('a', 'b', 'c', 'd')) # 元组tuple,元素都不为空或0 True >>> all(('a', 'b', '', 'd')) # 元组tuple,存在一个为空的元素 False >>> all((0, 1, 2, 3)) # 元组tuple,存在一个为0的元素 False >>> all([]) # 空列表 True >>> all(()) # 空元组 True
any:可迭代对象中,有一个True就是True
zip:拉链方法,返回的是一个迭代器。
函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,
然后返回由这些元祖组成的内容,如果各个迭代器的元素个数不一致,则按照长度最短的返回
l1 = [1,2,3,4]
tu1 = ('小旋风','男神','女神')
set = {'*','**','***','****'}
obj = zip(l1,tu1,set) #返回的是元组。(1,‘小旋风’,‘**’)...
for i in obj:
print(i)
map:循环模式 将第二个可迭代对象的元素 作为第一个函数的参数进行循环执行 map(function,iterable)
def func2(x):
return x**2
obj = map(func2,[1,2,3,4])
for i in obj:
print(i)
def func2(x,y):
return x+y
obj1 = map(func2,[1,2,3,4],(2,3,4,5))
for i in obj1:
print(i)
filter:筛选模式,filter(function,iterable) 根据前面function的条件,对后面iterable可迭代对象进行筛选。
def func(x):return x%2 == 0
ret = filter(func,[1,2,3,4,5,6,7])
print(ret)
for i in ret:
print(i)
结合lambda函数 可以更简写为ret = filter(lambda x:x%2==0,[1,2,3,4,5,6,7])
lambda 匿名函数,一句话函数 和map filter函数相结合。
def func(x):return x % 2 == 0
def func1(x,y):
return x + y
ret = lambda x,y:x+y #lambda就相当于def ret相当于函数名
ret(2,3) #执行 相当于 func(x)
print(ret(2,3))
def func1(x,y):
return x if x >f else y
ret = lambda x,y:x if x>y else y #三符运算
print(ret(2,3))
ret1= lambda x:x%2==0
ret=filter(ret1,[1,2,3,4,5,6,7])
for i in ret:
print(i)
#内置函数:map filter sorted max min
#匿名函数:lambda
#递归函数:
***********************递归函数*********************
什么是递归函数:自己调用自己
def func1():
print(666)
func1()
func1() #自己调用自己,每次执行一次func1()会重新再开辟个空间
#默认最大递归深度998
count= 0
def func1(n):
n += 1
print(n)
func1(n)
func1(count)
#引用sys模块 设置最大递归深度
import sys
sys.setrecursionlimit(1000)
count= 0
def func1(n):
n += 1
print(n)
func1(n)
func1(count)
#求年龄
def age(n):
if n == 1:
return 23
else:
return age(n - 1) + 2
print(age(4))
**********************************二分查找是最简单的算法********************
1,数字序列,有序,不重复。
****************************************题************************************
#写函数,用户传入修改的文件名,与要修改的内容,执行函数,完成整个文件的批量修改操作。 #改的文件名是动态的 改的内容也是动态的。 import os with open('change.txt',encoding='gbk') as f1, open('change.bak',encoding='gbk',mode='w') as f2: for line in f1:#把f1类比成readlines new_line = line.strip().replace('alex','Sb') f2.write('%s ' %(new_line)) os.remove('change.txt') os.rename('change.bak','change.txt')
#使用函数传参的方法来讲更改的值变动可以活动 不再是写死 import os def modilication(path,old_content,new_content): #哪里需要改 就传进去那个参数path是字符串 with open(path,encoding='gbk') as f1, open(path+'.bak',encoding='gbk',mode='w') as f2: for line in f1:#把f1类比成readlines new_line = line.strip().replace(old_content,new_content) f2.write('%s ' %(new_line)) os.remove(path) os.rename(path+'.bak',path) modilication('change.txt','人','dog')
#读取下面函数执行后的值 a =10 b =20 def test5(a,b): print(a,b) c = test5(b,a) print(c) #这个就是要注意的函数没有返回值 那么打印出来的是None
#下面代码打印的结果分别是 #如果函数的默认参数是可变的数据类型,无论使用多少次这个默认参数 都是一个 def extendList(val,list=[]): list.append(val) return list list1 = extendList(10) list2 = extendList(123,[]) list3 = extendList('a') print('list1 = %s'%list1) #[10,'a'] print('list2 = %s'%list2) #[123] print('list3 = %s'%list3) #[10,'a']
#给每个函数写一个记录日志的功能 #功能要求:每一次调用函数之前,要将函数名称,时间节点记录到log的日志中。 #所需模块: #import time #struct_time = time.localtime() #print(time.strftime('%Y-%m-%d %H:%M:%S',struct_time)) import time def wrapper(f): def inner(*args,**kwargs): with open('log',encoding = 'utf-8',mode='a') as f1: struct_time = time.localtime() time_now = time.strftime('%Y-%m-%d %H:%M:%S',struct_time) f1.write('在%s,执行了%s '%(time_now,f.__name__)) ret = f(*args,**kwargs) return ret return inner @wrapper def func1(): time.sleep(0.2) print(666) @wrapper def func2(): time.sleep(0.4) print(666) @wrapper def func3(): time.sleep(0.6) print(666) func1() func2() func3()
比较优秀的题
#在编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件), #要求登录成功一次,后续的函数都无需要再输入用户名和密码。这个作业之上升级操作: #设置两套密码,一套为京东账号密码,一套为淘宝账号密码保存在文件中。 #设置四个函数,分别代表 京东首页 京东超市 淘宝首页 淘宝超市 #循环打印四个选项:京东首页,京东超市,淘宝首页,淘宝超市。 #供用户选择,用户输入选项后,执行该函数,四个函数都加上认证功能, #只要登录成功一次,在选择其他函数,后续都无需输入用户名和密码。 #相关提示,用带参数的装饰器,装饰器内部加入判断,验证不同的账号密码。 dic_status = { 'username':None, 'status':False } def login(f): #装饰器完成的就是登陆认证功能,用if来判断是否需要登录 def inner(*args,**kwargs): i=1 if dic_status['status']: ret = f(*args,**kwargs) return ret else: #满足三次登录 while i<4: username = input('请输入用户名:').strip() password = input('请输入密码:').strip() with open('register.txt' ,encoding='gbk',mode='r') as f1: #从记录密码账户的文件中提取出数据与输入的密码账号就行匹配 for line in f1: #这里是文件句柄是迭代器 惰性机制 不会取重。 list_line= line.strip().split(',') if username == list_line[0] and password ==list_line[1]: print('登录成功') #匹配成功 dic_status['username']= username dic_status['status'] =True ret = f(*args,**kwargs) return ret #return True #(直接一个return 结束函数,如果加break,这里只是跳出for训话,还有个while循环需要跳出, #所以return比较好,但是还有个问题如果这里用return True跳出循环 在下面taobao()函数不会执行,因为在第一次装饰是status=False,所以用上面的办法,重新再更新下 返回ret) else:#如果都没有登录成功 print('请重新登录,还剩%s机会'%(3-i)) i += 1 return inner @login def taobao(): print('欢迎%s来到淘宝首页....'%(dic_status['username'])) @login def taobao_shop(): print('欢迎%s来到淘宝超市首页....'%(dic_status['username'])) @login def JD(): print('欢迎%s来到京东首页....'%(dic_status['username'])) @login def JD_shop(): print('欢迎来到京东超市首页') #设置username的作用 taobao() taobao_shop() JD() JD_shop()
通过用户的选择进行登录
延续上面的更改如下:
while True: print(''' 1,访问淘宝 2,访问淘宝超市 3,访问京东 4,访问京东超市 ''') choice = input('请输入你的选项':).strip() if choice.isdigit(): choice = int(choice) if 0<choice<5: if choice == 1: taobao() elif... #这样写很啰嗦,还需要优化 else: print('超出范围,请重新输入') else: print('有非数字元素,请重新输入')
dic = { 1:taobao, 2:taobao_shop, 3:JD, 4:JD_shop, } #几个字典 可以剩很多事 记住这个方法 while True: print(''' 1,访问淘宝 2,访问淘宝超市 3,访问京东 4,访问京东超市 ''') choice = input('请输入你的选项:').strip() if choice.isdigit(): choice = int(choice) if 0<choice<=len(dic): dic[choice]() else: print('超出范围,请重新输入') else: print('有非数字元素,请重新输入')
下面是两个账户密码一个是京东的一个是淘宝的,分开。这个时候需要使用带参数的装饰器,这时候需要两个文件一个存京东的账户密码,一个存淘宝的账户密码
dic_status = { 'username':None, 'status':False } def login_out(argv): def login(f): #装饰器完成的就是登陆认证功能,用if来判断是否需要登录 def inner(*args,**kwargs): i=1 if dic_status['status']: ret = f(*args,**kwargs) return ret else: #满足三次登录 while i<4: username = input('请输入用户名:').strip() password = input('请输入密码:').strip() if argv == '淘宝': with open('淘宝.txt',encoding='gbk')as f1: line_list = f1.read().strip().split(',') if username == line_list[0] and password == line_list[1]: dic_status['username'] = username dic_status['status'] =True print('登录成功') ret = f(*args,**kwargs) return ret else: print('账号密码错误,还剩%s机会'%(3-i)) i += 1 elif argv == '京东': with open('京东.txt',encoding='utf-8')as f1: line_list = f1.read().strip().split(',') if username == line_list[0] and password == line_list[1]: dic_status['username'] = username dic_status['status'] =True print('登录成功') ret = f(*args,**kwargs) return ret else: print('账号密码错误,还剩%s机会'%(3-i)) i += 1 return inner return login @login_out('淘宝') def taobao(): print('欢迎%s来到淘宝首页....'%(dic_status['username'])) @login_out('淘宝') def taobao_shop(): print('欢迎%s来到淘宝超市首页....'%(dic_status['username'])) @login_out('京东') def JD(): print('欢迎%s来到京东首页....'%(dic_status['username'])) @login_out('京东') def JD_shop(): print('欢迎来到京东超市首页') #设置username的作用 dic = { 1:taobao, 2:taobao_shop, 3:JD, 4:JD_shop, } #几个字典 可以剩很多事 记住这个方法 while True: print(''' 1,访问淘宝 2,访问淘宝超市 3,访问京东 4,访问京东超市 ''') choice = input('请输入你的选项:').strip() if choice.isdigit(): choice = int(choice) if 0<choice<=len(dic): dic[choice]() else: print('超出范围,请重新输入') else: print('有非数字元素,请重新输入')
可以进行优化。
dic_status = { 'username':None, 'status':False } def login_out(argv): def login(f): #装饰器完成的就是登陆认证功能,用if来判断是否需要登录 def inner(*args,**kwargs): i=1 if dic_status['status']: ret = f(*args,**kwargs) return ret else: #满足三次登录 while i<4: username = input('请输入用户名:').strip() password = input('请输入密码:').strip() with open(argv+'.txt',encoding='gbk')as f1: line_list = f1.read().strip().split(',') if username == line_list[0] and password == line_list[1]: dic_status['username'] = username dic_status['status'] =True print('登录成功') ret = f(*args,**kwargs) return ret else: print('账号密码错误,还剩%s机会'%(3-i)) i += 1 return inner return login @login_out('淘宝') def taobao(): print('欢迎%s来到淘宝首页....'%(dic_status['username'])) @login_out('淘宝') def taobao_shop(): print('欢迎%s来到淘宝超市首页....'%(dic_status['username'])) @login_out('京东') def JD(): print('欢迎%s来到京东首页....'%(dic_status['username'])) @login_out('京东') def JD_shop(): print('欢迎来到京东超市首页') #设置username的作用