可变长参数,函数的对象,函数嵌套,名称空间和作用域
1.可变长参数
可变长参数:在调用函数时,参入的参数个数可以不固定。
调用函数时,传值的方式莫非两种,一种是位置实参,另一种是关键字实参,因此形参也必须的有两种解决方式,以此来分别接收溢出的位置实参(*)也关键字实参(**)
一.可变长形参之*
形参中的会将溢出的位置实参全部接收,然后存储元组的形式,然后把元组赋值给后的参数。需要注意的是:*后的参数名约定俗成为args。
def sum(*args):
res = 0
for sum_new in args:
res += sum_new
return res
res = sum(1,2,3,4,5)
print(res) # 15
二 .可变长实参之*
实参中的, 会将后参数的值循环取出,达三成位置实参。以后但凡碰到实参中带的,他就是位置实参,应该马上打散成位置实参去看。
def func(a,b,*,c,d):
print(a,b)
print(c,d)
lst = [10,11]
''' *lst 相当于把列表中的每一个元素单独拿出来,扔到参数中进行赋值;'''
func(*lst,c=12,d=13) # func(10,11,c=12,d=13)
列二:
def func(x, y, z, *args):
print(x, y, z, args)
func(1, *(1, 2), 3, 4)#1,1,2,(3,4)
三. 可变长实参之**
形参中的** 会将溢出的关键字实参全部接收,然后存储字典的形式,然后把字典赋值给** 后的参数。需要注意的是:**后的参数名约定俗成为kwargs。
def func(**kwargs):
print(kwargs)
func(a=5) #{"a":5}
四 .可变长实参之**
实参中的,会将** 后参数的值循环取出,打散成关键字实参。以后但凡碰到实参中带**的,它就是关键字实参,应该马上打散成关键字实参去看。
def func(x,y,z,**kwargs):
print(x,y,z,kwargs)
func(1,2,3,**{"a":1,"b":2})
#1 2 3 {'a': 1, 'b': 2}
列er:深
def func(a,b,*,c,d):
print(a,b)
print(c,d)
dic = {"c":12,"d":13}
''' **dic 相当于把字典当中每一个元素变成 c=12,d=13 的形式传入关键字实参 '''
func(10,11,**dic) #func(10,11,c=12,d=13)
五 .可变长参数的列题应用。
ndex(name ,age,hobby):
print(f"name:{name},age:{age},hobby:{hobby}")
def guess(*args,**kwargs):
print(f'args:{args}')
print(f'kwargs:{kwargs}')
index(*args,**kwargs)
guess(name="liangjing",age="20",hobby="购物,run,听音乐")
#args:()
#kwargs:{'name': 'liangjing', 'age': '20', 'hobby': '购物,run,听音乐'}
#name:liangjing,age:20,hobby:购物,run,听音乐
六. 关键字形参
现在有一个需求,函数的使用者必须按照关键字实参传。
def register(x, y, **kwargs):
if 'name' not in kwargs or 'age' not in kwargs:
print('用户名和年龄必须使用关键字的形式传值')
return
print(kwargs['name'])
print(kwargs['age'])
register(1, 2, name='liangjing', age=20)
#liangjing
# 20
命名关键字形参:在函数定义阶段,*后面的参数都是命名关键字参数。
特点:在传值时,必须按照key=value的方式传值,并且key必须命名关键字参数的指定的参数名。
2.函数的对象
函数是第一类对象,即函数可以被当做数据处理。
一 . 函数对象的四大功能。
1.引用
def f1():
print('from f1')
# 函数对象 == 函数名 # 函数名()就是在调用,没有其他的意思
# 1. 引用
func = f1
print('f1:', f1) #f1: <function f1 at 0x0000019E710BA1F8> f1打印地址
print('func:', func) #func: <function f1 at 0x0000019E710BA1F8>
func()# from f1
2.当作容器类元素
def f1():
print('from f1')
# 函数对象 == 函数名 # 函数名()就是在调用,没有其他的意思
# 1. 引用
lt = [f1, 1, 2, 3]
print('lt[0]', lt[0]) #lt[0] <function f1 at 0x000002628657A1F8>
print('f1', f1)#f1 <function f1 at 0x000002628657A1F8>
lt[0]()#form f1
- 当作函数参数
def f1():
print('from f1')
def f2(f2_f1):
print('f2_f1',f2_f1)
f2_f1()
f2(f1)
print('f1', f1)
#f2_f1 <function f1 at 0x000001A00C79A318>
#from f1
# f1 <function f1 at 0x000001A00C79A318>
- 当作函数的返回值
def f1():
print('from f1')
def f2(f2_f1):
return f2_f1
res = f2(f1)
print('res', res) #res <function f1 at 0x000001D6E002A168>
print('f1', f1) #f1 <function f1 at 0x000001D6E002A168>
res() #from f1
3.函数对象的练习。
def register():
print('register')
def login():
print('login')
def withdraw():
print('wightdraw')
def shopping():
print('shopping')
func_dict = {
'1': register,
'2': login,
'3': withdraw,
'4': shopping,
}
print('''
1 注册
2 登录
3 提现
4 购物
''')
while True:
choice = input('请选择你需要的功能(输入q退出):')
if choice == 'q':
break
func_dict[choice]()
4.函数嵌套。
一 . 函数的嵌套定义
#定义函数,只检测语法,不会执行代码
def f1():
def f2():
print('from f2')
f2()
f2() # NameError: name 'f2' is not defined(报错:没有定义f2)
定义后
def f1():
def f2():
print('from f2')
f2()
f1() #from f2
现在有个需求,通过函数做一个九九乘法表。
#方法
# # 打印九九乘法表
# for i in range(1, 10): # i控制的是行
# for j in range(i): # j控制的是列
# print(f'{j+1}*{i}={(i)*(j+1)}', end=' ')
#
# print() # 打印换行
#
# '''
# i控制行,j控制列
# i = 1 for j in range(1) j = 0 + 1
# i = 2 for j in range(2) j = 0 + 1 j = 1 + 1
# i = 3 for j in range(3) j = 0 + 1 j = 1 +1 j = 2 + 1
# i = 4
# i = 5
##################################################################
# 函数的定义
#def 函数名(等同于变量名)():
# """对函数(工具)的描述信息"""
# 代码块
def chengfabiao():
"""描述的信息"""
for i in range(1, 10):
for j in range(1, i + 1):
print("%d*%d=%2d " % (i, j, i * j), end="")
print()
# 函数的调用
chengfabiao()
chengfabiao()
chengfabiao()
#结果:
1*1= 1
2*1= 2 2*2= 4
3*1= 3 3*2= 6 3*3= 9
4*1= 4 4*2= 8 4*3=12 4*4=16
5*1= 5 5*2=10 5*3=15 5*4=20 5*5=25
6*1= 6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36
7*1= 7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49
8*1= 8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64
9*1= 9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81
二 .函数的嵌套调用。
def max2(x, y):
if x > y:
return x #return是一个返回值,默认返回较大的数。
else:
return y
def max4(a, b, c, d):
res1 = max2(a, b)
#调用值max4拿到res1中,这时a=1,b=2带max2中进行比较x=1,y=2,此时y大,res1=2(b),将b(b=2)带入res2进行比较,依次比较下去。
res2 = max2(res1, c)
res3 = max2(res2, d)
return res3
print(max4(1, 2, 3, 4)) #4
列:
# 定义函数,只检测语法,不会执行代码
def f1():
print('from f1')
def f2():
print('from f2')
res = f1()#打印的是外部的值 from f1
# ***:函数内部定义的函数,外部不能用
5.名称空间和作用域
变量名/函数名 --》 名称 --》=专门存储名称的
名称空间(name spaces):在内存管理那一章节时,我们曾说到变量的创建其实就是在内存中开辟了一个新的空间。但是我们一直在回避变量名的存储,其实在内存中有一块内存存储变量名与变量间的绑定关系的空间,而这个空间称为名称空间。(存储了内置方法的名称)
一. 内置名称空间
内置名称空间:存放Pyhton解释器自带的名字,如int、float、len
生命周期:在解释器启动时生效,在解释器关闭时失效
二.全局名称空间(执行文件代码的时候才会有全局)
全局名称空间:除了内置和局部的名字之外,其余都存放在全局名称空间,如下面代码中的x、func、l、z
生命周期:在文件执行时生效,在文件执行结束后失效
x = 1
def func():
pass
l = [1, 2]
if 3 > 2:
if 4 > 3:
z = 3
三 . 局部名称空间 (函数调用的时候才会有局部)
局部名称空间:用于存放函数调用期间函数体产生的名字,如下面代码的f2
生命周期:在文件执行时函数调用期间时生效,在函数执行结束后失效。
def f1():
def f2():
print('from f2')
f2()
f1()
四. 加载顺序
由于.py文件是由Python解释器打开的,因此一定是在Python解释器中的内置名称空间加载结束后,文件才开始打开,这个时候才会产生全局名称空间,但文件内有某一个函数被调用的时候,才会开始产生局部名称空间,因此名称空间的加载顺序为:内置--》全局--》局部。
五 .搜索顺序:先从当前所在位置寻找,找不到再按照这种顺序,不会逆着方向寻找 局部 --》 全局 --》 内置 --》 报错
二 . 作用域
一.作用域:域指的是区域,作用域即作用的区域。
二 .全局作用域
全局作用域:全局有效,全局存活,包含内置名称空间和全局名称空间。
# 全局作用域
x = 1
def bar():
print(x)
bar() #1
三 .局部作用域
局部作用域:局部有小,临时存储,只包含局部名称空间。
# 局部作用域
def f1():
def f2():
def f3():
print(x)
x = 2
f3()
f2()
f1() #2
四. 注意点需要注意的是:作用域关系在函数定义阶段就固定死了,与函数的调用无关。
x = 1
def f1(): # 定义阶段x=1
print(x)
def f2():
x = 2
f1()
f2()#1 与函数的调用无关
五. 函数对象+作用域应用
# 作用域应用
def f1():
def inner():
print('from inner')
return inner
f = f1() # 把局部定义的函数放在全局之中
def bar():
f()
bar() #from inner
六 . global关键
修改全局作用域中的变量。
七. nonlocal 关键字
修改局部作用域中的变量。
八 .注意点
3.3 注意点
- 在局部想要修改全局的可变类型,不需要任何声明,可以直接修改。
- 在局部如果想要修改全局的不可变类型,需要借助global声明,声明为全局的变量,即可直接修改。
lis = []
def f1():
lis.append(1)
print(f"调用函数前: {lis}")
f1()
print(f"调用函数后: {lis}")
#调用函数前: []
#调用函数后: [1]