函数的优势:
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 = [1,2,3,4,1,6,9,10]
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‘)
补充:
#如果函数的默认参数是可变的数据类型,无论使用多少次默认参数,都是一个,并且如果默认参数是{} [] ()等 实参位置不用加索引
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]
名称空间,加载顺序等
当程序运行时,代码从上至下以此读取,遇到变量与值,它会在内存中开辟一个空间,存放变量与值得内存地址的对应关系。
这样存储变量与值得对应关系的空间叫做名称空间(命名空间)又称全局名称空间
当解释器遇到函数时,它会将函数名存储在内存中,但是对于函数体漠不关心。
当函数执行时,它会通过函数名找到函数体,然后将函数体里面的变量等对应关系存放在一个临时开辟的空间中,随着函数的结束,临时的空间关闭,这样的空间叫做临时名称空间。
另一点:函数中的变量只能在函数内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。
内置名称空间:代码需要解释器来读取,运行需要添加到内存
如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)
*****************************闭包*************************
内层函数对外层函数非全局变量的引用就叫闭包
判断是不是闭包,是函数名.__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 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)
******************************装饰器********************************
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()
*******************函数的有用信息**************************
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__) #打印函数内容
********************带参数的装饰器****************************
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,先执行 time_out(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函数名 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()
补充知识点:
装饰器的本质是闭包
开放封闭原则(写代码最好不要写死):允许代码扩展,添加新功能,如果函数交给其他人使用,所以进行封闭
#局部作用域可以对全局作用域的变量进行引用:举例说明 count = 1 def func1(): print(count) func1() #函数内部如果有变量名与全局变量名相同,且对此变量进行改变,python就会将你引用的变量视为局部定义的变量,但是局部没有定义,那么他会报错。 count = 1 def func1(): count = count + 1 #会报错 print(count) func1() #这种情况也会报错,要理解,都是一个道理 def func1(): count = 3 def inner(): 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__
可迭代对象转为迭代器:可迭代对象.__iter__()
迭代器定义:内部含有__iter__() 和__next__()
判断是不是迭代器:__iter__在不在dir(对象中)
isinstance方法检测。
迭代器优点:节省内存
惰性机制
单项不可逆
可迭代对象:内部含有__iter__方法的就是可迭代对象,遵循可迭代协议。
用dir函数可以检测:
print(dir(‘123‘))#返回的是列表
print(‘__iter__‘ in dir([1,2,3])) 用这个方法判断列表中是否存在__iter__
迭代器:可迭代对象通过.__iter__()可以转化成迭代器,遵循可迭代协议,其实它也是数据类型。
内存占用非常小;节省内存;满足惰性机制,在循环到第四次时,再次循环它会从第5次继续执行;迭代器是未来做数据类型;取值过程单项不可逆的首选方式
l = [1,2,3]
l_obj = l.__iter__() #生成迭代器
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
怎样判断是否是迭代器:
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
*************************生成器******************************
生成器的本质也是迭代器,生成器是自己用python写的迭代器。
1,通过生成器函数构建。
2,通过生成器推导式构建。
1)函数构建生成器
def func1(): #函数体就是生成器函数
print(666)
yield 222
yiels 777
g_obj = func1() #生成器对象 在下面通过func1().__next__()执行这个函数
print(g_obj.__next__()) #生成器也是.__next__()取值 一个yield对应一个.__next__()
print(g_obj.__next__())
生成器作用的说明举例:
def cloth2():
for i in range(1,10001):
yield ‘衣服%s‘ %i
g = cloth2()
for i in range(1,51):
print(g.__next__())
for i in range(1,151):
print(g.__next__())
#send的用法
def func1():
count = yield 222
print(count)
yield 777
yield 888
g_obj = func1()
print(g_obj.__next__())
print(g_obj.send(2))
1)send 具有next功能,一次只能执行一个yield
2)send 可以给上一个yield传值 传什么值都行
3)第一个取值不能使用send
4)最后一个yield不会得到send的值
2)推导器构建
原文:https://www.cnblogs.com/xiao-xuan-feng/p/12173041.html