函数
函数基础
定义
空函数/有参函数/无参函数
定义格式:
def function_name():
code
空函数:()
内不定义变量,函数体为pass
有参函数:()
内有定义变量,叫做形参,用来接收实参,函数体不为空
无参函数:()
内不定义变量,函数体内有代码
参数
形参
定义函数时给出, 用来接收实参值,没有实际意义
位置形参
在()
内,从左到右,一个一个按照位置定义变量
def function1(name,age,hobbly_list):
print(f'{name},{age},{hobbly_list}')
默认形参
给位置形参赋值一个初始或者叫默认的值,即为默认形参
注意点:
1、第一个默认形参的左边必须全部是位置形参,右边必须全部是默认形参
2、默认形参的值只在定义阶段赋值一次,也就是说默认参数的值在函数定义阶段就已经固定了
3、默认参数的值通常应该是不可变类型(当为可变类型时候,参数值的变化会被保存)
def function2(name,age=15,hobbly_list):
print(f'{name},{age},{hobbly_list}')
实参
调用函数时给出,传给所调用的函数的形参变量,是一个具体的值
位置实参
在()
内按照形参的位置一个一个填写实参值,传给位置形参
function1('alex',18,['music','jiao'])
关键字实参
以赋值的形式传入参数值,赋值对象为所调用函数的形参变量名
注意点:
1、第一个关键字实参的左边必须全部是位置实参,右边必须全部是关键字实参
2、关键字实参之间可以不按照顺序传入
function1(name,age=25,hobbly_list=['music','jiao'])
function2(hobbly_list=['music','jiao'],name='egon',age=28)
可变长参数
1、定义形参时,*args
会将溢出的位置实参全部接收,以元组的形式存储赋值给args
,这里是约定俗成用args
,实际上只是变量名
2、定义形参时,**kwargs
会将溢出的关键字实参全部接受,以字典的形式存储赋值给kwargs
,这里是约定俗成用kwargs
,实际上只是变量名
3、调用函数传入实参时,*
会将*
后的参数的值循环取出,打散成位置实参。以后但凡碰到实参中带*的,它就是位置实参,应该马上打散成位置实参去看。
4、调用函数传入实参时,**
会将参数的值循环取出,打散成关键字实参。以后但凡碰到实参中带**的,它就是关键字实参,应该马上打散成关键字实参去看。
实例:
定义函数
# 定义形参*agrs,用于接收溢出的位置实参
def function1(name, age, *args):
print(f'name,age:{name},{age}')
print(f'args:{args}')
print('从args中逐个取出:')
for item in args:
print(item)
# 定义形参**kwargs,用于接收溢出的关键字实参
def function2(name, age=19, **kwargs):
print(f'name,age:{name, age}')
print(f'kwargs:{kwargs}')
print('从kwargs中逐个取出:')
for key in kwargs :
print(f'key:{key},value:{kwargs[key]}')
调用函数:不定数量的位置实参
# 调用函数,传入不定数量的位置实参
function1('egen', 15, 'music', 'jiao', 'laugh')
'''
输出结果:
name,age:egen,15
args:('music', 'jiao', 'laugh')
从args中逐个取出:
music
jiao
laugh
'''
# 调用函数,以元组的形式传入数量不定的位置实参,*后跟元组变量名
tup = ('dapao', 'jiao', 'han')
function1('nick',3,*tup)
'''
输出结果:
name,age:nick,3
args:('dapao', 'jiao', 'han')
从args中逐个取出:
dapao
jiao
han
'''
调用函数:不定数量的关键字实参
# 调用函数,传入不定数量的关键字实参
function2('alex',25,hobby1='musci',hobby2='jiao',hobby3='read')
'''
输出结果:
name,age:('alex', 25)
kwargs:{'hobby1': 'musci', 'hobby2': 'jiao', 'hobby3': 'read'}
从kwargs中逐个取出:
key:hobby1,value:musci
key:hobby2,value:jiao
key:hobby3,value:read
'''
# 调用函数,以字典的形式传入数量不定的关键字实参,**后跟字典变量名
hobby_dic = {'hobby5':'music','hobby2':'read','hobby3':'jiao'}
function2('robot',6,**hobby_dic)
'''
输出结果:
name,age:('robot', 6)
kwargs:{'hobby5': 'music', 'hobby2': 'read', 'hobby3': 'jiao'}
从kwargs中逐个取出:
key:hobby5,value:music
key:hobby2,value:read
key:hobby3,value:jiao
'''
返回值
返回值可以赋值给变量
1、函数具有返回值,若没有指定,默认返回值为None
2、返回单个值,可以是任意类型,返回的类型跟要返回的数据类型一致
3、可以返回多个值,用逗号隔开,以元组的形式返回
4、当执行return时,返回值的同时,函数被终止
def function2(name,age=15,hobbly_list):
if age < 18:
return name,age,hobbly_list # 程序终止,以元组的形式返回多个值
elif age <25 :
return hobbly_list# 程序终止,返回hobbly_list,不改变数据类型
else:
return None
调用
function_name()
即调用函数,执行函数体代码,直到碰到return或者执行完函数体内所有代码结束。
三种形式调用函数:
def max_self(x,y):
if x>y:
return x
else:
return y
# 1.
max_self(1,2)
# 2.
res = max_self(1,2)*12
# 3.
max_self(max_self(20000,30000),40000)
函数对象
函数时第一类对象,即函数可以被当作数据处理或者说把它当作function类型的数据类型
该数据类型支持的方法就是函数体内写好的功能,值为函数返回值
def function_name():
print('from function_name')
print(function_name,type(function_name),id(function_name))
'''
输出结果:
<function function_name at 0x000001EF48803F78> <class 'function'> 2127225175928
'''
函数对象四大功能:
引用
# 跟基本数据类型的引用一样,使用赋值符号,赋值给一个变量名
f1 = function_name # 引用函数,等同于重命名了一个相同的函数为f1
f1() # 引用之后,可以用f1作为函数名调用函数
f2 = function_name() # 引用函数返回值
print(f1,f2)
'''
输出结果:
<function function_name at 0x0000028C8BC13F78> None
'''
当作参数传给一个函数
# 注意的是,定义形参为function类型,那么就能接收function类型的实参
def foo(m):
m()
foo(function_name)
# 如果给函数foo添加引用f,那么f也能作为参数传给一个可接受function类型实参的函数
f= foo
f(function_name)
当作函数的返回值
# 基本数据类型都能被函数作为返回值return,函数或者函数的引用的数据类型为function,也可以当作返回值
def function2(x):
return x # 此函数中x没有数据类型限制,接收什么类型,就返回什么类型,包括function
function2(function_name)
# 同样适用于函数引用
f2 = function_name
function2(f2)
作为容器类型的元素
# 作为字典的value
function_dic = {'1':function_name,'2':f2,'3':foo}
# 作为列表元素
function_list = [function_name,f1,foo,f2]
函数嵌套
def func():
print('from func')
def func1():
print('from func1')
func1()
func()
'''
from func
from func1
'''
名称空间/作用域
内置名称空间
python启动时,开辟内存空间,存放python内置方法名称,如len/print/for
等等
Pyhton被关闭时失效
全局名称空间
python文件被执行时,开辟内存空间,除了内置和局部的名称之外,其余名称都存放在全局名称空间
Python文件执行结束时失效
局部名称空间
执行文件调用函数时,开辟内存空间,存放函数中命名的变量名,函数名
函数调用结束时失效
加载顺序
内置--》全局--》局部
查找顺序
由于名称空间是用来存放变量名与值之间的绑定关系的,所以但凡要查找名字,一定是从三者之一找到,查找顺序为:
从当前的所在位置开始查找,如果当前所在的位置为局部名称空间,则查找顺序为:局部--》全局--》内置。
全局作用域
全局有效,全局存活,包括内置名称空间和全局名称空间
x = 1
def func():
print('from func')
print(x)
func()
'''
from func
1
'''
局部作用域
局部有效,临时存储,只包含局部名称空间
x = 1
def func():
print('from func')
x = 10
print(x)
fun()
print(x)
'''
from func
10
1
'''
作用域的查找执行顺序
- 局部作用域与全局作用域没有关系
- 可变类型数据作用域为:局部+全局
global
:声明变量作用域为全局nonlocal
: 声明变量作用域为包含该变量所在局部名称空间的所有局部名称空间,即包含当前局部的所有局部
函数进阶
闭包函数
意义:将函数和函数需要用到的参数包裹到一起,这样当调用函数时,该参数的第一查找作用域为被包在一起参数
def func1(x):
print(x)
def outer(x):
def inner():
print(x)
return inder
func = outer(x)
func() # 此时不需要再传参数,以后调用直接使用即可
装饰器
用闭包的思想,将被装饰的函数与装饰的功能包在一起
要求:
1、不能使得被装饰函数的源代码改变
2、不得更改被装饰函数的调用方式
无参装饰器
即被装饰的函数无参
def index():
print('A')
def deco(func):
def wrapper():
# 在wrapper中对真正的func函数做功能添加
res = func() # 调用被装饰函数
return res
return wrapper
# 调用
index = deco(index) # 参数为函数对象,重命名装饰之后的wrapper 为原来的函数名,左到以假乱真,实现不改变调用方式的作用
index()
有参装饰器
即被装饰的函数有参
def index(x):
print(x)
def deco(func):
# 无论原来的func有多少参数,都能接收
def wrapper(*args,**kwargs):
# 在wrapper中对真正的func函数做功能添加
res = func(*args,**kwargs) # 调用被装饰函数
return res
return wrapper
# 调用
index = deco(func)
index(x)
多层装饰器
当内层装饰器需要参数时,仍然使用闭包思想,将装饰器函数与参数包在一起
def index(x):
print(x)
def sanceng(x):
def deco(func):
def wrapper(*args,**kwargs):
# 在wrapper中对真正的func函数做功能添加
res = func(*args,**kwargs) # 调用被装饰函数
return res
return wrapper
return deco
# 调用
index = sanceng(x)(func)
index()
装饰器语法糖
在定义被装饰函数时,在它的上面一行使用格式@装饰器名字
@deco
def index(x):
print(x)
如果是多层装饰器,则使用格式@装饰器名字(x)
,注意传参(实参)
@sanceng(19)
def index(x):
print(x)
迭代器
迭代的工具,基于上一次的结果,重复进行某种操作
可迭代对象
python中一切为对象(数据类型)
其中含有__iter__
内置方法的,叫做可迭代对象
如str,list,tuple,dict,set,file
都是可迭代对象
调用__iter__
方法后得到的返回值,拥有__next__
方法,即为迭代器对象
迭代器对象
其中含有__iter__
和__next__
内置方法的,叫做迭代器对象
目前所学,只有file
是同时含有__iter__
和__next__
方法,是迭代器对象
迭代器的用法
调用一次__next__
方法,遍历一个元素
for循环原理
lt = [1,2,3]
lt_iter = lt.__iter__
while True:
try:
print(lt.iter.__next__)
except Expection:
break
生成器
使用定义函数的方式,自定义一个迭代器,即为生成器
def g():
yield 1
yield 2
yield 3
print(g)
g = g()
print(g)
'''
<function g at 0x00000248B8872F78>
<generator object g at 0x00000248BFACBA48>
'''
# 迭代循环
for i in g:
print(i)
'''
1
2
3'''
用生成器定义一个range()方法
'''
1、参数可能是关键字实参
2、可能有位置实参和关键字实参组合
'''
起始结束全部为位置实参,步长默认1,起始默认0
def range(stop,start=0,end=2):
i = 0
while True:
if i < start:
i += 1
continue
if i >= stop:
break
yield i
i += end
for i in range(1,5):
print(i)
起始结束不全是位置实参,步长默认1(大于等于三参数版本,小于三参数的未写)
def range(*args, **kwargs):
start = 0
stop = 1
end = 1
if len(args) + len(kwargs) > 3:
return
if len(args) == 0:
for k, v in kwargs.items():
if k not in ['start', 'stop', 'end']:
break
if k == 'start':
start = v
if k == 'stop':
stop = v
if k == 'end':
end = v
# 拿到start,stop,end之后
i = 0
while True:
if i < start:
i += 1
continue
if i >= stop:
break
yield i
i += end
elif len(args) == 1:
start = args[0]
for k, v in kwargs.items():
if k not in ['stop', 'end']:
break
if k == 'stop':
stop = v
if k == 'end':
end = v
# 拿到start,stop,end
i = 0
while True:
if i < start:
i += 1
continue
if i >= stop:
break
yield i
i += end
elif len(args) == 2:
if 'end' not in kwargs:
return
end = kwargs['end']
start, stop = args
# 拿到start,stop,end
i = 0
while True:
if i < start:
i += 1
continue
if i >= stop:
break
yield i
i += end
elif len(args) == 3:
start, stop, end = args
i = 0
while True:
if i < start:
i += 1
continue
if i >= stop:
break
yield i
i += end
花里胡哨表达式
三元表达式
条件成立时的返回值 if 条件 else 条件不成立时的返回值
# 函数版
def (x,y):
if x > y:
return x
return y
# 表达式版
res = x if x > y else y
列表推导式
按照某种规律生成一个列表
lis = [i**2 for i in range(10)]
print(lis)
'''
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
'''
字典生成式
按照某种规律生成一个字典
dic = {'a'*i:i**2 for i in range(10)}
print(dic)
'''
{'': 0, 'a': 1, 'aa': 4, 'aaa': 9, 'aaaa': 16, 'aaaaa': 25, 'aaaaaa': 36, 'aaaaaaa': 49, 'aaaaaaaa': 64, 'aaaaaaaaa': 81}'''
递归
递归思想
一种特殊的函数嵌套调用:函数定义时,间接或者直接调用自身
- 每调用一次,下一层的问题规模将会减小
- 必须有条件使得函数不再调用自身,即开始回溯之前重复调用的函数
def print_user(n):
print(n,'from print_user')
n += 1
if n > 100:
return
print_user(n)
print_user(20)
汉诺塔问题
分析:
第一步:假设已经将上面的n 层 从 a -> c : 先将上面n-1层转移到b,那么再把第n层转移到c,再把b上的n-1层移动到c即可,第二步再考虑怎么将b上的n-1层转移到c
第二步:将上面的n-1 层 从 b -> c : 假设已经将上面n-2层转移到a,再把第n-1层转移到c,再把a移动到c,第三步再考虑怎么将a上的n-2层转移到c
第三步开始重复第一步:将上面的n-2层从 a -> c :
...
直到最后:将上面的2层从a->c
def hanot(a,b,c,n):
if n == 2: #最后一步
print(f'{a}->{b}')
print(f'{a}->{c}')
print(f'{b}->{c}')
return
hanot(a,c,b,n-1) # 第一步
print(f'{a}->{c}') # 第二步
hanot(b,a,c,n-1) # 第三步
匿名函数
有名函数
def func():
pass
匿名函数
lambda 参数:返回值
匿名函数就是没有函数名的函数对象
# 一次传入多个参数,返回函数对象
res = lambda x,y:x if x>y else y
print(res,type(res))
print(res(1,6))
'''
<function <lambda> at 0x0000023D82082F78> <class 'function'>
6
'''
一般不单独使用,常与其他函数连用
salary_dict = {
'nick': 3000,
'jason': 100000,
'tank': 5000,
'sean': 2000
}
max()
单独一个可迭代对象作为参数
# 单独一个可迭代对象作为参数
res1 = max(salary_dict)
# 单独一个可迭代对象iter作为参数,并传入一个函数对象fun,迭代取出la中的元素传入func,将func的返回值作为比较值,最终返回最大值在la中对应的元素
def func(k):
return salary_dict[k]
res2 = max(salary_dict,key=func)
lt = [2,3,5,6,8]
res3 = max(lt,key=lambda item: item if item > 2 else 10)
print(f'res1:{res1},res2:{res2},res3:{res3}')
'''
res1:tank,res2:jason,res3:2'''
大于等于两个参数
res = max(1,2,5,3,7,6)
map()
第一个参数为函数对象func
,第二个参数为可迭代对象iter
,结果为可迭代对象map object
- 首先将可迭代对象变成迭代器对象
- 通过
__next__
迭代取出迭代器可迭代对象中的一个个元素i
,依次作为参数传给func(i)
,得到一个个结果res
存放到可迭代对象map object
中
lt = [1,2,3,4]
# 使用有名函数对象作为参数
def func1(x):
return x**2
res = map(func1,lt)
print(list(res))
# 使用匿名函数作为参数(匿名函数就是没有函数名的函数对象)
res = map(lambda x:x**3,lt)
print(list(res))
'''
[1, 4, 9, 16]
[1, 8, 27, 64]
'''
filter()
通过func
筛选
第一个参数为函数对象func
或None
,第二个参数为可迭代对象,返回结果为可迭代对象filter object
- 首先将可迭代对象变成迭代器对象
- 如果第一个参数是
None
,则将所有的元素存放到filter object
中 - 如果第一个参数为函数对象
func
,通过__next__
迭代取出迭代器可迭代对象中的一个个元素i
,依次作为参数传给func(i)
,得到一个个结果res
- 如果
res
为True
,就存放到可迭代对象filter object
中
lt = [1,2,3,4]
def func1(x):
if x > 2:
return x**2
res1 = filter(func1,lt)
res2 =filter(None,lt)
print(list(res1),list(res2))
'''
[3, 4] [1, 2, 3, 4]'''
sorted()
内置函数
enumerate()
带有索引的迭代
l = ['a','b','c']
for item in l:
print(item)
'''
(0, 'a')
(1, 'b')
(2, 'c')'''
eval()
把字符串翻译成数据类型,不能为空
自动去除空格,换行符
str_list = "[1,2,3,4] \n"
data = eval(str_list)
print(data,type(data))
'''
[1, 2, 3, 4] <class 'list'>'''
其他
bytes()
:转化为二进制编码
chr()/ord()
:参考ASCII码表将数字转成对应字符/将字符转换成对应的数字
divmod()
:分栏
hash():是否可哈希