形参角度
3.万能参数:*args 接受所有的位置参数 ; **args 接受所有的关键字参数。定义一个函数时,传入万能参数,就可以接收任意的参数(位置实参、关键字实参)以供以后为函数添加功能,开放接口。
def func(*args,**kargs):
return args,kargs
#如此定义一个函数,就可以接收任意的参数(位置实参、关键字实参)以供以后为函数添加功能,开放接口。
知识点补充:
编写代码原则:开放封闭原则,代码对于未来的一些拓展一定是要开放的(具有接口功能)可以添加一些功能。而有时需要对函数传入的参数的数量不同,需要进行改进,因此需要一种形参,可以接受所有的实参(位置实参,关键字实参),即万能参数(常用*args, **args 表示)。
‘ * ’的魔术用法:在函数的定义时, 星号代表聚合;在函数的调用时,星号代表打散。
‘ * ’的聚合:用于万能形参;约定俗称万能形参的形式为'*+args '或' **+kargs ',args与kargs可以换成别的参数,但最好不要换。
‘*args’ 可以接受所有位置参数。它将所有的位置实参聚合成一个元祖,将这个元祖赋值给args。
#写一个函数,计算传入任意数字的和
def func(*args):
c = 0
for i in args:
c += i
return c
print(funs(1,2,3,4,5)) #15
‘**kargs’ :可以接受所有的关键字实参.在函数的定义时, * *将所有的关键字参数聚合成一个一个字典,将这个字典赋值给kargs。
def func(**kargs):
print(kargs)
funs(number='980173694',massage='欢迎加入百度网盘群')
>>>{'number': '980173694', 'massage': '欢迎加入百度网盘群'}
‘ * ’的打散:在函数的调用时 *代表打散。
在函数的调用时一个*打散的是迭代对象,必须用在可迭代对象上(str,list等)
def func(*args):
print(args)
funs([1,2,3],[4,5,6])
>>>([1, 2, 3], [4, 5, 6])
funs(*[1,2,3],*[4,5,6]) #*的打散,等同于funs(1, 2, 3, 4, 5, 6)
>>>(1, 2, 3, 4, 5, 6)
在函数的调用时两个*打散的是字典。**
def func(*args,**kargs):
print(kargs)
print(args)
funs({'number': '980173694', 'massage': '欢迎加入百度网盘python学习资源群'})
>>>{}
({'number': '980173694', 'massage': '欢迎加入百度网盘python学习资源群'},)
funs(**{'number': '980173694'},**{'massage': '欢迎加入百度网盘python学习资源群'})
#**的打散,等同于funs(number='980173694', massage='欢迎加入百度网盘python学习资源群')
>>>{'number': '980173694', 'massage': '欢迎加入百度网盘python学习资源群'}
()
4.仅限关键字参数(3.4版本以后):只能接受关键字参数。若设置此参数必须给此参数传入值,否则会报错。此参数只能在*args与**kargs之间。
def func(*args,a,**args): #a:仅限关键字参数
print(a)
funs(a = '山就在那儿')
>>>山就在那儿
形参的顺序
调用函数时,会自动从左至右传入参数,除万能参数外,实参与形参会一一对应。因此如果形参的顺序安排不得当,函数调用就会出现问题,甚至会报错。因此,形参的顺序如下:1.位置参数 2.*args 3.默认参数 3. 仅限关键字参数 4.**kargs。 读者可以自行尝试其他顺序,自行找出为什么不能用其他的顺序。 仅限关键字参数与默认参数可互换。
def func(a,b,*args,sex='男',c,**kargs):
pass #a,b:位置参数 c:仅限关键字参数
命称空间角度:在python解释器开始执行之后, 就会在内存中开辟一个空间, 每当遇到一个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表示这个函数存在了, 至于函数内部的变量和逻辑, 解释器是不关心的. 也就是说一开始的时候函数只是加载进来, 只有当函数被调用和访问的时候, 解释器才会根据函数内部声明的变量来进行开辟变量的内部空间,函数中的变量只能在函数内部使用。随着函数执行完毕, 这些函数内部变量占用的空间也会随着函数执行完毕而被清空.
我们给这个‘存放名字与值的关系’的空间起了一个名字-------命名空间。
一个py文件中,存放变量与值的关系的一个空间叫做全局名称空间,而当执行一个函数时,内存中会临时开辟一个空间,临时存放函数中的变量与值的关系,这个叫做临时名称空间,或者局部名称空间。
python还有一个空间叫做内置名称空间:内置名称空间存放的就是一些内置函数等拿来即用的特殊的变量:input,print,list等等
全局名称空间:代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间;
即 我们直接在py文件中, 函数外声明的变量都属于全局命名空间。程序不结束,全局名称空间不会消失。
局部命名空间:在函数的运行中开辟的临时的空间叫做局部命名空间也叫做临时名称空间 ,
即在函数中声明的变量会放在局部命名空间。函数结束时局部命名空间就会消失。
内置名称空间:存放python解释器为我们提供的名字, list, tuple, str, int这些都是内置命名空间
加载顺序:所谓的加载顺序,就是这三个空间加载到内存的先后顺序,也就是这个三个空间在内存中创建的先后顺序,他们不能能同时创建,在启动python解释器之后,即使没有创建任何的变量或者函数,还是会有一些函数直接可以用的比如abs(-1),max(8,3)等等,在启动Python解释器的时候,就已经导入到内存当中供我们使用,所以肯定是先加载内置名称空间,然后就开始从文件的最上面向下一行一行执行,此时如果遇到了初始化变量,就会创建全局名称空间,将这些对应关系存放进去,然后遇到了函数执行时,在内存中临时开辟一个空间,加载函数中的一些变量等等。所以这三个空间的加载顺序为:内置命名空间(程序运行伊始加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载。
取值顺序:取值顺序就是引用一个变量,先从哪一个空间开始引用。这个有一个关键点:从哪个空间开始引用这个变量。我们分别举例说明:
# 如果你在全局名称空间引用一个变量,先从全局名称空间引用,全局名称空间如果没有,才会向内置名称空间引用。
input = 123
print(input) # 123
# 如果你在局部名称空间引用一个变量,先从局部名称空间引用,
# 局部名称空间如果没有,才会向全局名称空间引用,全局名称空间再没有,就会向内置名称空间引用。
input = 123
print(input) # 123
def func():
input = 111
print(input) # 111
func()
所以空间的取值顺序与加载顺序是相反的,取值顺序满足的就近原则,从小范围到大范围一层一层的逐步引用且单向不可逆,即LEGB原则。(L:lcoal E:eclose G:global B:builtin)
作用域:作用域就是作用范围, 按照生效范围来看分为全局作用域和局部作用域
全局作用域: 包含内置命名空间和全局命名空间. 在整个文件的任何位置都可以使用(遵循从上到下逐⾏执行)。作⽤域命名空间:全局作用域: 全局命名空间 + 内置命名空间
局部作用域: 在函数内部可以使用。作⽤域命名空间:2. 局部作⽤域: 局部命名空间
局部作用域可以引用全局作用域的变量但不能改变全局变量(当python解释器读取到局部作用域时,若发现有对一个变量进行修改的操作,解释器会认为你在局部已经定义过这个局部变量了,于是就在局部找这个局部变量,若没有就会报错,无法改变全局变量),全局作用域不可义引用局部作用域的变量。
内置函数: globals() local() 注意与关键字(global、nonlocal)的区别。详细请看明天的python基础学习day11。
globals(): 以字典的形式返回全局作用域(内置命称空间以及全局命称空间的所有内容)所有的变量对应关系。
locals(): 以字典的形式返回当前作用域的变量所有的对应关系。
a = 1
b = 2
print(globals())
>>>{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.sourcefileloader object at>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'D:/PycharmProjects/untitled/texr3/题.py', '__cached__': None, 'a': 1, 'b': 2}
print(locals())
>>>{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.sourcefileloader object at>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'D:/PycharmProjects/untitled/texr3/题.py', '__cached__': None, 'a': 1, 'b': 2}
# 在局部作用域中打印。
a = 1
b = 2
def func():
c = 3
print(globals()) # 和上面一样,还是全局作用域的内容
print(locals()) # {'c': 3}
funs()
函数的嵌套(高阶函数):有列表的嵌套:列表的嵌套就是一个列表中还有列表,可能那个列表中还有列表......那么顾名思义,函数的嵌套,就是一个函数中,还有函数。
关键点:只要遇见了函数名+()就是函数的调用. 如果没有就不是函数的调用。那么现在举例练习:请说出下面代码的执行顺序:
#例1:
def func1():
print('in func1')
print(3)
def func2():
print('in func2')
print(4)
func1()
print(1)
func2()
print(2)
# 例2:
def func1():
print('in func1')
print(3)
def func2():
print('in func2')
func1()
print(4)
print(1)
func2()
print(2)
# 例3:
def func2():
print('1in func2')
def func3():
print('in func3')
print('2in func2')
func3()
print('3in func2')
print(3)
func2()
print(5)
若果您对文章内容有疑问,请在评论区留言,如果您有想要补充的新想法,新方法,也请在评论区留言。以期大家共同成长,共同进步。
设计者开发时总的指导思想是,对于一个特定的问题,只要有一种最好的方法来解决就好了。这在由Tim Peters写的Python格言(称为The Zen of Python)里面表述为:There should be one-- and preferably only one --obvious way to do it. 这正好和perl语言(另一种功能类似的高级动态语言)的中心思想TMTOWTDI(There's More Than One Way To Do It)完全相反。