之前学函数总觉都不系统,这次整理结合了太白金星的博客,但是是经过自己理解,精简语言之后的内容。
函数:
def f_name(p1, p2, *args, p3=None, p4, **kwargs):
...##此处是函数体
return
'''
函数到return结束
1、return后面无值,则函数执行到这里结束。
2、return后面一个值,则返回这个值,函数结束。
3、return后面你有多个返回值,则整体以一个元组的形式返回,可以在调用处使用结构接收返回值,函数结束。
'''
调用处:
1、位置参数
f_name(p1, p2, p3)
'''
按照参数的先后顺序传递参数
'''
2、关键字参数
f_name(p3 = v1, p1 = v2, p2 = v3)
'''
按照参数的名字传参,不用考虑顺序(v1、v2和v3是参数值)
'''
3、两者结合使用:
f_name(p1, p3 = v3, p2 = v2)
'''
结合使用时,必须位置参数在前,关键字参数在后。
'''
实例:
def func(p1, p2, *args, p3=3, p4, **kwargs):
print(p1, p2)
print(args)
print(p3, p4)
print(kwargs)
return
func(1, 2, 3, 4, 5, p3=6, p4=7, a=1, b=2, c=3)
输出:
1 2
(3, 4, 5)
6 7
{'a': 1, 'b': 2, 'c': 3}
函数定义处参数(形参):
1、位置参数:
形如:p1, p2。只接收一个量的参数,比如一个整数,一个小数,一个字符串,一个列表,一个元组。。。
def f_name(p1, p2):
...
return
2、默认值参数:
形如p3=3,即在定义时就给变量一个默认值,这个值在实参处若给出则默认值被覆盖,不给出则使用默认值
def f_name(p=3):
...
return
1)f_name() #这样调用时p的值默认为3
2)f_name(1) 或者 f_name(p=1) #这样调用时p的值为1
3、动态参数一,动态接收位置参数args:
形如args,即在定义时加上号的参数,约定俗成一般使用args做参数名,即args,可以接受很多调用处的位置参数,并且将他们打包成元组(这里是因为*的“魔力”),传给args,且这种参数只能定义一个
def f_name(*args):
print(args)
return
f_name(1, 2, 3, 4)
输出:
(1,2,3,4)
4、动态参数二,动态接收关键字参数:
形如kwargs,即在定义时加上号的参数,约定俗成的一般使用kwargs做参数名,即**kwargs,可以接收很多调用处的关键字参数,并且把他们打包成字典,传给kwargs,且这种参数只能定义一个
def f_name(**kwargs):
print(kwargs)
return
f_name(a=1, b=2)
输出:
{'a': 1, 'b': 2}
5、python3新增参数类型,仅限关键字参数(不常用):
形如p4,定义在动态接受位置参数之后,动态接收关键字参数(若有的话)之前,和默认值参数没有位置约束。接收且必须以关键字形式接受一个实参
def f_name(*args, f3=3, f4):
print(args)
print(p3, p4)
return
或者:
def f_name(*args, f4, f3=3):
print(args)
print(p3, p4)
return
f_name(1, 2, 3, p3=4, p4=5)
输出:
(1, 2, 3)
4 5
参数的顺序:
位置参数,动态接收位置参数args, 默认值参数, 【仅限关键字参数】, 动态接收关键字参数。
讨论:
首先位置参数肯定放在第一位的
然后动态接收位置参数args和默认值参数谁在前呢? 肯定是args,假设默认值参数在前,如果使用位置传参数的话,本来想传给args的第一个参数会把默认值参数覆盖掉他的默认值。
之后是默认值参数和动态关键字参数谁在前呢?当然是默认值参数,假设**kwargs在前,则所有的关键字参数都会传给它,而默认值参数就永远该变不了默认值了。
最后仅限关键字参数是一定在args和**kwargs中间的,它和默认值参数的顺序不受约束
*号的“魔力”:
*号的聚合和打散作用:
*号的聚合作用
刚刚我们说到的形参使用时,即args,会把传过来的位置参数打包成元组,传给args,这是一种聚合
还有**kwargs,会把传过来的关键字参数打包成字典传给kwargs
实例,聚合作用:
1)
def f_name(*args):
print(args)
return
f_name(1, 2, 3, 4)
输出:
(1,2,3,4)
2)
def f_name(**kwargs):
print(kwargs)
return
f_name(a=1, b=2)
输出:
{'a': 1, 'b': 2}
*号的打散作用:
另一种用法就是如果我们两个列表list1和list2,想把他们的元素传给*args怎么办到呢?
可以使用下面这种办法:
def f1(*args):
print(args)
return
l1 = [1, 2, 3]
s1 = '456'
f1(*l1, *s1)##对列表、字符串、元组等迭代类型都可以
输出:
(1, 2, 3, '4', '5', '6')
另外一种情况,就是我们有两个字典d1和d2,想把他们的元素传给**kwargs怎么办到呢?
办法类似:
def f2(**kwargs):
print(kwargs)
return
d1 = {
'a' : 1,
'b' : 2
}
d2 = {
'3' : 3,
'4' : 4
}
f2(**d1, **d2)
输出:
·{'a': 1, 'b': 2, '3': 3, '4': 4}
这是在函数传参数时,那么在函数外面呢?
函数外的聚合和打散:
**1、聚合(处理剩下的元素)**
我们这样用过:
a, b = (1, 2)
我们还可以这样用:
1)
a, *b = [1, 2, 3, 4, 5]
print(a, b)
输出:
1 [2, 3, 4, 5]
2)
*a, b = [1, 2, 3, 4, 5]
print(a, b)
输出:
[1, 2, 3, 4] 5
**2、打散:**
list1 = [1, 2, 3, 4, 5]
print(*list1)
输出:
1 2 3 4 5
#### 总结:即*可以用来将剩下的元素聚合到一起,也可以将一个迭代对象打散。非常灵活。
名称空间:
python在运行开始时会在内存中开辟一段空间,程序运行时遇到一个变量时就会把变量和值之间的关系记录下来;遇到函数定义时,只把函数名记录下来,不管函数内部的变量和逻辑。 只有当遇到函数调用的时候,python解释器会再开辟一块内存空间,用来存储函数的变量,且这些变量只能在函数内部使用,随着函数执行完毕,这块内存也会被清空。
全局命名空间:
我们称存储变量和值关系的空间为命名空间,程序开始时的命名空间为全局命名空间。
局部命名空间:
在函数运行时临时开辟的空间叫做局部命名空间,也叫临时名称空间
内置名称空间:
在python执行时有一些我们没有定义,却拿来即用的变量和函数,如input,print,list,str等,这些存放在内置命名空间中。
加载顺序:
首先在程序运行开始,内置名称空间内的东西是拿来即用的,肯定首先加载进来,然后遇到变量声明,函数定义,需要创建全局命名空间,之后每次遇到函数调用,都会为每个函数创建临时命名空间。所以加载顺序应该是:
内置命名空间 -->> 全局命名空间 -->> 临时命名空间1 -->>临时命名空间2 …
取值顺序:
取值顺序采取就近原则,从最近的命名空间一步步到最外面的命名空间,所以顺序为:
临时命名空间 -->> 全局命名空间 -->> 内置命名空间。
作用域:
作用域就是作用范围,分为全局作用域和局部作用域
全局作用域:全局命名空间+内置命名空间
局部作用域:局部命名空间
内置函数:
globals(): # 以字典的形式返回全局作用域所有的变量对应关系。
locals(): #以字典的形式返回当前作用域的变量的对应关系。
这里一个是全局作用域,一个是当前作用域,一定要分清楚,接下来,我们用代码验证:
# 在全局作用域下打印,则他们获取的都是全局作用域的所有的内容。
a = 2
b = 3
print(globals())
print(locals())
'''
{'__name__': '__main__', '__doc__': None, '__package__': None,
'__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001806E50C0B8>,
'__spec__': None, '__annotations__': {},
'__builtins__': <module 'builtins' (built-in)>,
'__file__': 'D:/lnh.python/py project/teaching_show/day09~day15/function.py',
'__cached__': None, 'a': 2, 'b': 3}
'''
# 在局部作用域中打印。
a = 2
b = 3
def foo():
c = 3
print(globals()) # 和上面一样,还是全局作用域的内容
print(locals()) # {'c': 3}
foo()
改变变量的作用域:
#先看一个现象:
a = 1
def func():
print(a)
func()
a = 1
def func():
a += 1 # 报错
func()
也就是说全局变量在局部作用域不能做修改,只能读。
一、python提供了一个关键字可以实现在局部作用域对全局变量做修改:global
#1、global的第一个功能,使全局变量可以在局部作用域修改:
count = 1
def search():
global count
count = 2
search()
print(count)
#2、global的第二个功能是,声明一个全局变量:
def func():
global a
a = 3
func()
print(a)
global的功能:
1、声明一个全局变量
2、在局部作用域修改全局变量,需要用global
二、python3新加的功能nonlocal(使用较少),功能和global类似,不过只对父级(且父级不能为全局作用域)作用域有效:
#例如:
def add_b():
##nonlocal 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
nonlocal功能:
1、不能更改全局变量
2、在局部作用域对父级作用域(或者更外层非全局作用域)的变量做修改,并且引用的哪层,从那层的及以下子层的变量全部发生改变。