形参角度:
万能参数
如果我们在传参数时不很清楚有哪些,或者说给一个函数传了很多实参,考虑用动态参数也叫万能参数。
万能参数,即动态参数,分为两种:动态接收位置参数 *args,动态接收关键字参数**kwargs.
#动态接收位置参数 *args
*【动态接收位置参数 *args:*args,约定俗成在*后使用args,PEP8规范中规定就使用args (这里起到魔法效果的是 * 而不是args)】
*【函数定义时,*代表聚合,它将所有的位置参数聚合成一个元组,赋值给了args,*args可以接受所有的位置参数。】
*【总结:*args只可以接受多个位置参数,并且返回一个元组。不能接受关键字参数。】
def eat(a,b,c,d):
print('我请你吃:%s,%s,%s,%s'%(a,b,c,d))
eat('蒸羊羔','蒸熊掌','蒸鹿邑','烧花鸭')#我请你吃:蒸羊羔,蒸熊掌,蒸鹿邑,烧花鸭
def eat(*args):
print('我请你吃:',args) #返回一个元组
print('我请你吃:%s,%s,%s,%s'%args)
eat('蒸羊羔儿','蒸熊掌','蒸鹿尾儿','烧花鸭')##只能接受位置参数,不能接受关键字参数
#结果:
我请你吃: ('蒸羊羔儿', '蒸熊掌', '蒸鹿尾儿', '烧花鸭')
我请你吃:蒸羊羔儿,蒸熊掌,蒸鹿尾儿,烧花鸭
#练习:传入函数中数量不定的int型数据,函数计算所有数的和并返回。
def my_sum(*args):
sum = 0
print(*args)#1 2 3 4 5
print(args)#(1, 2, 3, 4, 5)
for i in args: #计数思想
sum += i
return sum
print(my_sum(1,2,3,4,5))#15
#动态接收关键字参数: **kwargs
*【动态接收关键字参数: **kwargs ,**kwargs接受所有的关键字参数,然后将其转换成一个字典赋值给kwargs这个形参。】
*【函数定义时:**将所有的关键字参数聚合到一个字典,将这个字典赋值给了kwargs】
*【总结:**kwargs只能接受多个关键字参数,然后将其转换成一个字典。不能接受位置参数。】
def fuc(**kwargs):
print(kwargs)
fuc(name = '太白',age = 18,sex = '男')#只能接受关键字参数,不能接受位置参数
#{'name': '太白', 'age': 18, 'sex': '男'}
#动态参数的完整写法:万能参数:*args, **kwargs,
*【如果一个参数设置了动态参数,那么它可以接受所有的位置参数,以及关键字参数,这样就会大大提升函数拓展性,针对于实参参数较多的情况下,解决了一一对应的麻烦。】
def fuc(*args,**kwargs):
print(args)
print(kwargs)
fuc('蒸羊羔儿','蒸熊掌','蒸鹿尾儿','烧花鸭','烧雏鸡','烧子鹅',name = '太白',age = 18,sex = '男')
#结果:
('蒸羊羔儿', '蒸熊掌', '蒸鹿尾儿', '烧花鸭', '烧雏鸡', '烧子鹅')
{'name': '太白', 'age': 18, 'sex': '男'}
*的魔性用法。
*的魔性用法:函数中分为打散和聚合。函数外可以处理剩余的元素。
###打散和聚合:
1.聚合:在函数定义时,*代表聚合 。在args前面加一个* ,那么args可以接受多个位置参数,并且返回一个元组.(**kwargs也是同理将多个关键字参数转化成一个字典返回),所以在函数的定义时: *起到的是聚合的作用。
2.打散:在函数调用时,*代表打散:在实参角度的位置参数-->(可迭代对象:字符串,列表。元组)前面加*,相当于把这些实参拆解成的一个个的组成元素当作位置参数,然后传给args 。
在实参角度的位置参数-->字典前面加**
# 在函数的调用时,*代表打散。
def func(*args,**kwargs):
print(args) # (1,2,3,22,33)
print(kwargs)
func(*[1,2,3],*[22,33])
# (1, 2, 3, 22, 33)
# {}
func(*'fjskdfsa',*'fkjdsal')
# ('f', 'j', 's', 'k', 'd', 'f', 's', 'a', 'f', 'k', 'j', 'd', 's', 'a', 'l')
# {}
func(**{'name': '太白'},**{'age': 18})
# ()
# {'name': '太白', 'age': 18}
##1:
针对*args,在实参角度的位置参数--可迭代对象(字符串,列表,元组)前面加*,相当于把这些实参拆解成的一个个的组成元素当作位置参数,然后传给args
'''你如何将三个数据(这三个数据都是可迭代对象类型)s1 = 'alex',l1 = [1, 2, 3, 4],tu1 = ('武sir', '太白', '女神',)的每一元素传给动态参数*args?'''
s1 = 'alex'
l1 = [1, 2, 3, 4]
tu1 = ('武sir', '太白', '女神',)
def fuc(*args):
print(args)
fuc(s1,l1,tu1)#('alex', [1, 2, 3, 4], ('武sir', '太白', '女神'))
fuc(*s1,*l1,*tu1)#('a', 'l', 'e', 'x', 1, 2, 3, 4, '武sir', '太白', '女神')
##2:
针对**kwargs,在实参角度的位置参数---字典前面加**,将字典变为关键字变量,再由**kwargs变为字典
##
def func(**kwargs):
print(kwargs)
dic1 = {'name': '太白', 'age': 18}
dic2 = {'hobby': '喝茶', 'sex': '男'}
func(**dic1,**dic2)
#结果:
{'name': '太白', 'age': 18, 'hobby': '喝茶', 'sex': '男'}
##
def func(*args,**kwargs):
print(args)
print(kwargs)
dic1 = {'name': '太白', 'age': 18}
dic2 = {'hobby': '喝茶', 'sex': '男'}
func(**dic1,**dic2)
#结果:
()
{'name': '太白', 'age': 18, 'hobby': '喝茶', 'sex': '男'}
func(dic1,dic2)
#结果:
({'name': '太白', 'age': 18}, {'hobby': '喝茶', 'sex': '男'})
{}
3.函数外处理剩余的元素。
a,b = (1,2)
print(a,b)#1 2
a,*b = [1,2,3,4]
print(a,b)#1 [2, 3, 4]
*rest,a,b =range(5)
print(rest,a,b)#[0, 1, 2] 3 4
print([1, 2, *[3, 4, 5]])#[1, 2, 3, 4, 5]
仅限关键字参数(了解)
*【形参角度第四个参数:仅限关键字参数,形参的仅限关键字参数只接受实参的关键字传的参数】
*【仅限关键字参数是python3x更新的新特性,他的位置要放在*args后面,**kwargs前面,类似于位置参数,它与默认参数的前后顺序无所谓,它只接受关键字传的参数】
def func(a,b,*args,sex= '男',c,**kwargs,): #与默认参数的前后顺序无所谓,他的位置要放在*args后面,**kwargs前面
print(a,b)
print(args) #元组
print(sex)
print(c) #形参的仅限关键字参数,只能接受实参的关键字参数
print(kwargs)
func(1,2,3,4,5,6,7,sex='女',name='Alex',age=80,c='666')
#结果:
1 2
(3, 4, 5, 6, 7)
女
666
{'name': 'Alex', 'age': 80}
def func(a,b,*args,d,sex = '男',**kwargs): #与默认参数的前后顺序无所谓,他的位置要放在*args后面,**kwargs前面
print(a,b)
print(args)
print(sex)
print(d)
print(kwargs)
func(1,2,3,4,5,d=1,sex='女',name='alex',age=80)
形参角度的参数的顺序。
形参角度最终的顺序为:
位置参数,*args,仅限关键字参数,默认参数,**kwargs
或: 位置参数,*args,默认参数,仅限关键字参数,**kwargs
#
位置参数必须在前面,即 :位置参数,默认参数
# *args 的位置?
【动态参数*args肯定不能放在位置参数前面,这样我的位置参数的参数就接收不到具体的实参了。】
【因为*args全部接收完了,所以动态参数必须在位置参数后面。】
【*args一定要在位置参数与默认值参数中间:位置参数,*args,默认参数】
#错:
def func(*args,a,b,sex= '男'):
print(a,b)
func(1,2,3,4)
# 错:args得到实参的前提,sex必须被覆盖了。
def func(a,b,sex= '男',*args,):
print(a,b)
print(sex)
print(args)
func(1,2,3,4,5,6,7,)
#对:
def func(a, b, *args, sex='男'):
print(a, b)
print(args)
print(sex)
func(1, 2, 3, 4, 5, 6, 7, sex='女')
#结果:
1 2
(3, 4, 5, 6, 7)
女
# **kwargs 位置?
def func(a,b,*args,sex= '男',**kwargs,):
print(a,b)
print(sex)
print(args)
print(kwargs)
func(1,2,3,4,5,6,7,sex='女',name='Alex',age=80)
# 1 2
# 女
# (3, 4, 5, 6, 7)
# {'name': 'Alex', 'age': 80}
2.名称空间。
全局名称空间,局部名称空间
存放名字与值的关系’的空间------>命名空间
全局命名空间:代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间;(py文件中,存放变量名与值的关系的一个空间叫做全局名称空间)
局部名称空间:当执行一个函数时,内存中会临时开辟一个空间,临时存放函数中的变量与值的关系,这个叫做临时名称空间【随着函数的执行的开始而创建,随着函数执行的结束而消失】
内置名称空间:内置名称空间存放的就是python源码给你提供的一些内置函数等拿来即用的特殊的变量:input,print,list等。
在python解释器开始执行之后, 就会在内存中开辟一个空间, 每当遇到一个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表示这个函数存在了, 至于函数内部的变量和逻辑, 解释器是不关心的. 也就是说一开始的时候函数只是加载进来, 仅此而已, 只有当函数被调用和访问的时候, 解释器才会根据函数内部声明的变量来进行开辟变量的内部空间. 随着函数执行完毕, 这些函数内部变量占用的空间也会随着函数执行完毕而被清空。
#名称空间;命名空间。
a = 1
b = 2
def func():
f = 5
print(f)
c = 3
func()
python分为三个空间:
# 内置名称空间(builtins.py)
# 全局名称空间(当前py文件)
# 局部名称空间(函数,函数执行时才开辟)
2.加载顺序,取值顺序。
# 加载顺序:
内置名称空间 ---> 全局名称空间 ----> 局部名称空间(函数执行时)
在启动python解释器之后,即使没有创建任何的变量或者函数,还是会有一些函数直接可以用的比如abs(-1),max(1,3)等等,在启动Python解释器的时候,就已经导入到内存当中供我们使用,所以肯定是先加载内置名称空间,然后就开始从文件的最上面向下一行一行执行,此时如果遇到了初始化变量,就会创建全局名称空间,将这些对应关系存放进去,然后遇到了函数执行时,在内存中临时开辟一个空间,加载函数中的一些变量等等。所以这三个空间的加载顺序为:内置命名空间(程序运行伊始加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载。
# 取值顺序(就近原则) 单向不可逆 LEGB原则
(从局部找时)局部名称空间 ---> 全局名称空间 ---> 内置名称名称空间
取值顺序就是引用一个变量,先从哪一个空间开始引用。这个有一个关键点:从哪个空间开始引用这个变量。
# 如果你在全局名称空间引用一个变量,先从全局名称空间引用,全局名称空间如果没有,才会向内置名称空间引用。
# 如果你在局部名称空间引用一个变量,先从局部名称空间引用。
# 局部名称空间如果没有,才会向全局名称空间引用,全局名称空间在没有,就会向内置名称空间引用。
所以空间的取值顺序与加载顺序是相反的,取值顺序满足的就近原则,从小范围到大范围一层一层的逐步引用。
input = '太白金星'
def func():
input = 'alex'
print(input)
func()
#alex
input = '太白金星'
def func():
input = 'alex'
func()
print(input)#太白金星
3.作用域。
# 两个作用域:
1.全局作用域 :内置名称空间 全局名称空间。在整个文件的任何位置都可以使用(遵循 从上到下逐⾏执行).
2.局部作用域:局部名称空间。在函数内部可以使用.
# 全局作用域只能引用全局作用域的变量。全局变量要放在py文件的开头。
# 局部作用域可以引用全局作用域的变量, 局部作用域不能改变全局变量。
date = '周五'
def func():
a = 666
print(date)#周五
print(a)#666
func()
print(a)#NameError: name 'a' is not defined
# 局部作用域不能改变全局变量。
局部作用域不能改变全局作用域的变量,当python解释器读取到局部作用域时,发现了你对一个变量进行修改的操作,
解释器会认为你在局部已经定义过这个局部变量了,他就从局部找这个局部变量,报错了。
#错【面试必考】
count = 1
def func():
count += 2
print(count)
func() # UnboundLocalError: local variable 'count' referenced before assignment 变量“count” 在创建前引用
# 局部作用域可以引用父级作用域的变量,但是不能改变父级作用域的变量。
def func():
count = 1
def inner():
print(count) #1
inner()
func()
#错【面试必考】
def func():
count = 1
def inner():
count += 1
print(count)
inner()
func()
#UnboundLocalError: local variable 'count' referenced before assignment
3.函数的嵌套(高阶函数)
# 例1:
def func1():
print('in func1')
print(3)
def func2():
print('in func2')
print(4)
func1()
print(1)
func2()
print(2)
# in func1 3 1 in func2 4 2
# 例2:
def func1():
print('in func1')
print(3)
def func2():
print('in func2')
func1()
print(4)
print(1)
func2()
print(2)
#1 in func2 in func1 3 4 2
# 例3:
def fun2():
print(2)
def fun3():
print(6)
print(4)
fun3()
print(8)
print(3)
fun2()
print(5)
#3 2 4 6 8 5
# glbals() locals()
这两个内置函数可以反映作用域的内容,有助于我们理解作用域的范围。
globals(): 以字典的形式返回全局作用域所有的变量对应关系。
locals(): 以字典的形式返回当前作用域的变量的对应关系。
4.内置函数 globals() locals()
a = 1
b = 2
def func():
name = 'alex'
age = 73
print(globals()) # 返回的是字典:字典里面的键值对:全局作用域的所有内容。
print(locals()) # 返回的是字典:字典里面的键值对:当前作用域的所有的内容。
print(globals()) # 返回的是字典:字典里面的键值对:全局作用域的所有内容。
print(locals()) # 返回的是字典:字典里面的键值对:当前作用域的所有的内容。
func()
5.关键字:nonlocal global。