函数
函数是带名字的代码块,它的定义方法是
def function(argument):
pass
return ret
#函数声明 函数名(参数)
# 函数执行过程
# 返回 返回值
函数是以功能为导向的,尽量不要在函数中插入print语句,
return 语句的作用:1、遇到return语句则结束函数
2、返回返回值:
不写return 返回 None
return None 返回 None
return 单个值 返回此单个值
return 多个值 将多个值包在元组中,返回给调用者此元组
函数的参数
函数的参数分为形参和实参,形参即我们定义函数时使用的形式参数,在真正调用函数时形参就会被实参取代,即我们真正用户函数的参数。
从实参的角度来说,函数的传参方法有如下几点:
按位置传参:实参和形参必须一一对应
按关键字传参
混合传参:关键字参数和未知参数混用,但位置参数必须在前面
从形参的角度来说,函数的传参方法有如下几点:
1,位置参数
2,默认参数,一般用于变化比较小的值
Ps:三元法则算法
#当我们用到 if else语句时,如果可以用如下方式一句代码实现一个简单的 if else功能
z = x if x> y else y
#当满足if else中间的条件时输出左边的x ,否则输出y
不定长参数:*args,**kwargs。
*args,即形参前面加*在形参位置,它可以接受任意长度的位置参数,并把它们放到为名args的一个元祖里。不过这里要注意,在形参里用的时候要放在默认参数的前面,而且要放在位置参数的后面,因为把它写在前面会导致写在后面的位置参数接收不到任何实参。
def func(a,b,c,*args,gender='male'):
print(a)
print(b)
print(c)
print(args)
print(gender)
func(1,2,'aaa','sss','mhbjh',gender='ddd')
#输出:1
2
aaa
('sss', 'mhbjh')
ddd
*args
**kwargs,在形参前面加**,它可以接受任意长度的默认参数,然后把对应的默认参数放在字典里。
def func(**kwargs):
print(kwargs)
func(a=1,b=4,c=3,d=2)
#输入:{'a': 1, 'b': 4, 'c': 3, 'd': 2}
**kwargs
形参的大致位置是,位置参数,*args,默认参数,**kwargs。不过很多时候我们用到的都是万能参数 def func(*args,**kwargs),这样可以接收任意数量的任意形式的参数,为以后拓展功能提供了参数入口。
Ps:魔法运算:打散
当我们使用万能参数的时候,*args总是把接收的实参变成一个元组,如果在实参的前面加入一个*,那么这个实参会被迭代着加入args形成的元组,不管他是列表,元组还是字符串,甚至是字典的所有的键。这里需要注意,*和**的意思是在执行程序时把可迭代对象参数打散,接收时是把散的参数聚合。
def func2(*args,**kwargs):
print(args)
l1 = [1,2,3]
l2 = {'a':4,'b':5,'c':6}
l3=(7,8,9)
l4='def'
func2(*l1,*l2,*l3,*l4)
#输出:(1, 2, 3, 'a', 'b', 'c', 7, 8, 9, 'd', 'e', 'f')
把 ** 加入到字典类型的形参前面,它会把实参字典中的键值对提取出来迭代着加入kwargs形成的字典中,使用时要注意两个不同的字典实参里的键不能有重复的,不然会报错。
def func3(*args,**kwargs):
print(kwargs)
dic = {'name1':'wang','age1':998}
dic1 = {'name2':'ba','age2':99}
func3(**dic,**dic1,woai='wojia')
#输出:{'name1': 'wang', 'age1': 998, 'name2': 'ba', 'age2': 99, 'woai': 'wojia'}
参数陷阱:当默认参数是一个可变数据类型时,如列表。
def defult_param(a,l = []):
l.append(a)
print(l)
defult_param('abc')
defult_param('123')
#输出:['alex']
['alex', 'egon']
#这里的 l 是一个默认形参,在这里每次调用函数都应该被重置成空列表,但是列表却每一次调用函数都加了一个元素。说明这个列表的内存元素一直在改变却没有被重置。
函数的嵌套
def f1():
a = 1
def f2():
def f3():
print(a)
f3()
f2()
f1()
#输出:1
名称空间
名称空间(命名空间)分为全局名称空间,局部名称空间和内置名称空间。
内置命名空间就是python解释器为我们提供的一些列名字,如input,len,print....等。
三种命名空间的加载顺序为:内置名称空间,全局名称空间,局部名称空间(执行函数时)。
而变量的取值顺序为:局部名称空间,全局名称空间,内置名称空间。
作用域:作用域就是作用范围,按照生效范围可以分为全局作用域和局部作用域。
全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效
局部作用域:局部名称空间,只能在局部范围内生效
globals(),locals()以字典的形式显示全局和局部的全部变量
c=5
d=99
def func():
a = 12
b = 20
d=88
print(locals())
print(globals())
func()
#输出:{'d': 88, 'b': 20, 'a': 12}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000018EA1F2A4E0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/Pycharm10/实验用/实验三.py', '__cached__': None, 'c': 5, 'd': 99, 'func': <function func at 0x0000018EA1ED3E18>}
global和nonelocal
global:1、声明一个全局变量 2、在局部作用域引用一个全局变量使用并改变它
#声明一个全局变量
def func():
global a
a = 3
func()
print(a)
#输出:3
#引用并改变一个全局变量
count = 1
def search():
global count
count = 2
search()
print(count)
#输出:2
global
另外,在局部引用全局中的可变数据类型列表和字典不用global
li = [1,2,3]
dic = {'a':'b'}
def change():
li.append('a')
dic['q'] = 'g'
print(dic)
print(li)
change()
print(li)
print(dic)
#输出:{'a': 'b', 'q': 'g'}
[1, 2, 3, 'a']
[1, 2, 3, 'a']
{'a': 'b', 'q': 'g'}
nonelocal:
1,不能修改全局变量。
2,在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。
def add_b():
b = 42
def do_global():
b = 10
print(b)
def dd_nonlocal():
nonlocal b
b = b + 20
print(b)
dd_nonlocal()
print(b)
do_global()
print(b)
add_b()
#输出:10
30
30
42 #这里输出42是因为nonelocal只对它的上一级起作用。
nonelocal
函数名的实质
函数名的实质就是函数的内存地址,
1、函数名可以赋值给其他变量
def f1():
print('f1')
func=f1
print(func()) #首先调用函数 func(),也就是f1(),然后打印f1()的返回值
#输出:f1
None
2、函数名可以当做容器类如列表、字典等数据类型的元素
def f1():
print('f1')
def f2():
print('f2')
def f3():
print('f3')
li=[f1,f2,f3]
dic={'f1':f1,'f2':f2,'f3':f3}
tu=(f1,f2,f3)
li[0]()
dic['f2']()
tu[2]()
#输出:f1
f2
f3
作为元素
3、函数名可以当做函数的参数和返回值
def f1():
print('f1')
def func1(argv):
argv()
return f1
f = func1(f1)
f()
#输出:f1
f1
Ps:函数名就是第一类变量
第一类对象(first-class object)指
1.可在运行期创建 2.可用作函数参数或返回值 3.可存入变量的实体。
闭包
内层函数对外层函数(非全局)的变量的引用,叫做闭包。
#不含参数的闭包
def wrapper():
name = 'ming'
def inner():
print(name)
inner()
print(inner.__closure__) # 检测是不是闭包 开头是cell 就是闭包
wrapper()
#输出:ming
(<cell at 0x0000018AA00395B8: str object at 0x0000018AA00C93B0>,)
#
name = 'ming'
def wrapper():
def inner():
print(name)
inner()
print(inner.__closure__) # 检测是不是闭包 开头是cell 就是闭包
wrapper()
#输出:ming
None
#含特定参数的闭包
def out(x):
def inner():
print(x)
inner()
print(inner.__closure__)
out('ming')
#输出:ming
(<cell at 0x0000024B243A95B8: str object at 0x0000024B24439570>,)
#带返回值的闭包
def out():
def inner():
print('ming')
return inner
out()() #out() 在这里代表 inner 这个函数名
#输出:ming
闭包的用处:如果说内存函数是个闭包,python内部有一个机制,遇到闭包,python会在内存中开启一个内存空间,且不会随着函数的结束而关闭,这样可以保护内存。
装饰器
装饰器是在不改变函数的原来内容的情况下,为函数增添额外的功能的一种函数方式
#简单的装饰器 在不改变函数func的情况下,测得函数运行时间
import time #调用time类
def func():
print('我爱中国')
def timmer(f):
def inner():
start_time = time.time() #记录开始时间
time.sleep(0.1) #程序延时0.1秒
f() #执行函数
end_time = time.time() #记录程序结束时间
print('----> 执行时间%s' % (end_time - start_time)) #打印程序运行时间
return inner
func = timmer(func) #这里的func相当于 inner
func() #这一句相当于 inner()
#输出:
我爱中国
----> 执行效率0.10044384002685547
@语法糖使用结构:
装饰器
@装饰器的函数名
要被装饰的函数
import time
def timmer(f):
def inner(*args,**kwargs): #推荐使用万能参数
start_time = time.time()
time.sleep(0.1)
ret = f(*args,**kwargs) #推荐使用万能参数
end_time = time.time()
print('----> 执行效率%s' % (end_time - start_time))
return ret #返回值
return inner #返回inner的函数名
@timmer # 语法糖 相当于func = timmer(func)
def func(a): #被装饰的函数
return 222
print(func('abc'))
#输出:----> 执行效率0.10044264793395996
222
#通用装饰器
def wrapper(func):
def inner(*args,**kwargs):
'''执行函数前操作'''
ret = func(*args,**kwargs)
'''执行函数后的操作'''
return ret
return inner
@wrapper
def func():
print(66)
func()
带参数的额装饰器:当我们需要装饰大量函数时,可通过带参数的装饰器来控制装饰器的开关。
# 带参数的装饰器
import time
flag = False #相当于装饰器的开关,当为True时装饰,False则不对函数进行装饰操作
def timmer_out(f):
def timmer(func):
def inner(*args,**kwargs):
'''执行函数前操作'''
if flag: #判断flag的内容来决定是否装饰
start_time = time.time()
time.sleep(0.3)
ret = func(*args,**kwargs)
'''执行函数后操作'''
end_time = time.time()
print('执行效率%s'%(end_time - start_time))
return ret
else:
ret = func(*args,**kwargs)
return ret
return inner
return timmer
@timmer_out(flag) # 1, timmer_out(flag) 返回 timmer --->
def f1(): # 2, @timmer 就是你认识的装饰器 f1 = timmer(f1)
print(666)
@timmer_out(flag) # f2 = timmer(f2)
def f2():
print(777)
f1()
f2()
#输出:666
777
带参数的装饰器
def outer(flag):
def timer(func):
def inner(*args,**kwargs):
if flag:
print('''执行函数之前要做的''')
re = func(*args,**kwargs)
if flag:
print('''执行函数之后要做的''')
return re
return inner
return timer
@outer(False)
def func():
print(111)
func()
控制装饰器开关的另一种方法
多个装饰器装饰一个函数时:
def wrapper1(func): # func = f
def inner1():
print('wrapper1 ,before func')
func()
print('wrapper1 ,after func')
return inner1
def wrapper2(func): # func = inner1
def inner2():
print('wrapper2 ,before func')
func()
print('wrapper2 ,after func')
return inner2
@wrapper2 # f = wrapper2(f) 里面的f == inner1 外面的f = inner2
@wrapper1 # f = wrapper1(f) 返回inner1 f = inner1
def f():
print('in f')
f() # inner2()
#输出:wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func
多个装饰器装饰一个函数
开放封闭原则
1、开放功能扩展:对以后的产品升级和功能扩展开放
2、封闭函数修改:在一个函数交付使用后,对它的改变可能引起连锁的影响使用这个函数的代码。
而装饰器完美的契合了以上开放封闭。
获取函数的有用信息
def f1():
'''
本函数的功能:绘图功能,实时接收数据并绘图.
return: 返回值
'''
print(f1.__doc__)
print(f1.__name__)
print(666)
print(f1.__doc__) #获取函数的说明信息
print(f1.__name__) # 获取函数的名字
f1()
#输出: 本函数的功能:绘图功能,实时接收数据并绘图.
return: 返回值
f1
本函数的功能:绘图功能,实时接收数据并绘图.
return: 返回值
f1
666
获取函数名和注释内容
def wrapper(func):
def inner(*args,**kwargs):
'''执行函数前进行的操作'''
ret = func(*args,**kwargs)
'''执行函数后进行的操作'''
return ret
return inner
@wrapper
def f1():
'''
本函数的功能:绘图功能,实时接收数据并绘图.
:return: 绘图需要的数据,返回给前端某标签
'''
print(f1.__doc__)
print(f1.__name__) ##这里我们想得到f1函数的信息,但是却得到了inner函数的信息
f1()
#输出:执行函数前进行的操作
inner
#下面调用funcrools的wraps模块输出f1的函数信息
from functools import wraps
def wrapper(func):
@wraps(func)
def inner(*args,**kwargs):
'''执行函数前进行的操作'''
ret = func(*args,**kwargs)
'''执行函数后进行的操作'''
return ret
return inner
@wrapper
def f1():
'''
本函数的功能:绘图功能,实时接收数据并绘图.
return: 绘图需要的数据,返回给前端某标签
'''
print(f1.__doc__)
print(f1.__name__)
f1()
#输出:
本函数的功能:绘图功能,实时接收数据并绘图.
return: 绘图需要的数据,返回给前端某标签
f1
装饰器获取原函数信息
生成器和迭代器
迭代器
判断可迭代对象和迭代器的方法一:
1、dir(被测对象) 如果它含有 __iter__而不含__next__,那么这个对象就是可迭代对象不是迭代器
2、dir(被测对象) 如果它既含有 __iter__又含有__next__,那么这个对象就是迭代器
a = 'abcdef'
b = (x*x for x in range(1,10)) #等号右边类似一个列表生成器,[x*x for x in range(10)]
print('__iter__' in dir(a))
print('__next__' in dir(b))
print('__next__' in dir(a))
print('__next__' in dir(b))
#输出:
True
True
False
True
#这说明a是可迭代对象不是迭代器,b是迭代器
迭代器和迭代对象判断方法一
方法二:
引用模块判断
from collections import Iterable
from collections import Iterator
a = 'abcdef'
b = (x*x for x in range(1,10)) #等号右边类似一个列表生成器,[x*x for x in range(10)]
print(isinstance(a,Iterable)) #检测是否是可迭代对象
print(isinstance(a,Iterator)) #检测是否是迭代器
print(isinstance(b,Iterable))
print(isinstance(b,Iterator))
#输出:
True
False
True
True
方法二
可迭代对象可以改变成迭代器
from collections import Iterable
from collections import Iterator
list=[1,2,3,4,5]
print(isinstance(list,Iterable))
print(isinstance(list,Iterator))
list=iter(list) #将可迭代对象 list 变换成迭代器。
#list=list.__iter__() 这句和上句效果一样
print(isinstance(list,Iterable))
print(isinstance(list,Iterator))
可迭代对象转换成迭代器
迭代器取值,__next__和next。因为迭代器是惰性的,每次调用只能取一个值
list=[1,2,3,4,5]
list=list.__iter__()
print(next(list)) #通过next(迭代器对象)取值
print(list.__next__()) #通过__next__对迭代器取值
print(next(list))
print(list.__next__())
print(next(list))
print(list.__next__()) #这一句时超出迭代器的取值范围,会报StopIteration的错误
#输出:
1
2
3
4
5
StopIteration的错误
迭代器取值
迭代器的意义:
1,迭代器节省内存。
2,迭代器惰性机制。
3,迭代器不能反复,一直向下执行.
for循环的机制.
内部含有__iter__方法,他会将可迭代对象先转化成迭代器.
,然后在调用__next__方法.
他通过 try...ecpect 语句处理异常。
可迭代对象:str list tuple dict set range
迭代器: 文件句柄
生成器
生成器的本质就是迭代器,生成器是自己用python代码写的迭代器.\
1,可以用生成器函数。
2,可以用各种推导式构建迭代器。
3,可以通过数据转化,将写的列表生成器的[]换成(),或者通过 iter 语句转换。list(),tuple()语句将生成器对象转化,一般不这样用,因为当数据量大时,会占用大量内存。
#生成器函数
def gener():
print('aaa')
yield 222
print('bbb')
yield 333
print('ccc')
g = gener()
print(next(g)) #调用函数运行到第一个yield
print(next(g)) #调用函数运行到第二个yield
#输出:
aaa
222
bbb
333
生成器构建
send和next函数
1,send 和next功能一样
2, 给上一个yiled和后面的值 整体发送一个值
send不能给最后一个yield发送值
获取第一个值的时候,不能用send 只能用next
def gener():
yield 222
count = yield 333
print('-------->',count)
yield 'aaa'
yield 'bbb'
g = gener()
print(g.send(None))
print(g.send('AAAA'))
print(g.send('wwwwww'))
#输出:
222
333
--------> wwwwww
aaa
send
列表生成式:
1、1到10的所有数的平方
l1=[x*x for x in range(1,11)]
2、1到10的所有数的平方是偶数的数(加判断)
l1=[x*x for x in range(1,11) if x%2==0]
3、找到嵌套的列表中含有两个‘m’的元素(双层循环)
li=[['11','ming','jiang','qqq','sss','misimy'],['mm','gg','mim','ss','mhom']]
l1=[x for i in li for x in i if x.count('m')==2]
print(l1)
#输出:
['misimy', 'mm', 'mim', 'mhom']
列表生成式和生成器表达式的区别:
1、列表生成式比较直观,占用内存;
2、生成器表达式不容易看出内存,省内存。
字典生成式:
将字典的key和value对调
dic = {'a':11,'b':22}
dic1 = {dic[k]:k for k in dic}
print(dic1)
#输出:
{11: 'a', 22: 'b'}
集合生成式:
计算每个值的平方并去重
set1 = {x*x for x in [1,-1,2,-2]}
print(set1)
#输出:
{1, 4}
内置函数
range(),input(),len(),print(),dir(),max(),min()等python内置函数,
#print()
print(1234)
print(5,6,7,8)
print(1234,end='') #print默认在后面加入‘\n’,此处为将换行符换为空
print(5,6,7,8,sep='*') #print可以在输出元素之间插入符号,默认是空格
#输出:
1234
5 6 7 8
12345*6*7*8
#print还可以往文件里写入,操作如下
f = open('file','w',encoding='utf-8')
print(666,file=f)
f.close()
#dir() 查找对象的所有方法
# print(dir([]))
#locals() 将局部的变量储存起来
#globals() 将全局的变量,函数名,等等 储存起来.
#数据类型的转换 list() str() tuple() set() dict() int()
# help(str) 将你查询的对象所有信息都拿出来.
# abs()取绝对值
#max()最大值,可设置函数
# ret = max(1,2,-3,key=abs)
# print(ret) #输出:-3 绝对值最大的数
#min()最小值
#sum (iterable,初始值)
# ret = sum([1,2,3],10)
# print(ret) #输出:16
#hash() 通过哈希表的规则,将对象转换成哈希值
#print(hash('fdsakf'))
#十进制转化成二进制
# print(bin(100))
# #将十进制转化成八进制
# print(oct(9))
# #将十进制转化成十六进制
# print(hex(33))
#float :有限小数,无线循环小数,不包含(无线不循环小数)
# print(1.35432,type(1.35432))
# print(float(3))
#divmod() 分页
# print(divmod(7,2)) #(商,余数)
#complex()复数
#enumerate(iterable,start 起始值) 枚举
# l = ['手机','电话']
# for i in enumerate(l):
# print(i)
s = "{'name':'alex'}"
s1 = "1+2+3+4"
#eval 有返回值 除去字符串两边的引号,返回里面的内容
#exec 没有返回值 除去字符串两边的引号,执行里面的代码
内置函数
内置函数链接: http://www.runoob.com/python/python-built-in-functions.html
filter:在return语句中写判断语句时,返回的是原参数,而不是True和False