Python函数
一、什么是函数
函数:对代码块和功能的封装和定义。简而言之就是定义⼀个事情或者功能, 等到需要的时候直接去⽤就好了。 那么这⾥定义的东⻄就是⼀个函数。
二、函数的定义及调用
# 函数的定义
def 函数名():
函数体
函数名:函数名的命名方法和变量命名一样,可以使用下划线连接命名和驼峰命名,推荐使用下划线连接命名。
函数体:就是函数被调用之后要执行的代码块。
接下来我们来定义一个简单的增进友谊的函数:
def add_friendship():
print("掏出手机")
print("启动探探APP")
print("挑选一个靓妹")
print('问:"晚上交流一下感情可好?"')
print("OK!开始谈人生。")
函数定义完之后发现并没有增进你们之间的友谊,因为函数定义完之后是不会执行的,还需要调用才会执行。
函数的调用:使用函数名可以调用函数。写法:函数名()
# 函数的调用
add_friendship() # 这个时候函数的函数体会被执行
# 掏出手机
# 启动探探APP
# 挑选一个靓妹
# 问:"晚上交流一下感情可好?"
# OK!开始谈人生。
这个时候友谊已经增进了一次了。如果还想再次增进友谊的话我们只需要再次调用这个函数就可以了。
比如:
add_friendship()
add_friendship()
add_friendship()
add_friendship()
add_friendship()
这个时候问题来了,增进友谊之后肯定会有收获吧,那该怎么表达你这次增进友谊所获得的的收获呢?
三、函数的返回值
执行完函数之后,我们可以使用return来返回一个结果。
return的使用:
def add_friendship():
print("掏出手机")
print("启动探探APP")
print("挑选一个靓妹")
print('问:"晚上交流一下感情可好?"')
print("OK!开始谈人生。")
return "今晚交流甚是欢悦"
ret = add_friendship()
print(ret) # 今晚交流甚是欢悦
# 这个时候就能得到你这次增进友谊所获得的收获了
注意:
- 如果没有return那么返回值则默认为None
- 如果返回值为空,即 return 后面什么也没有,那么返回值默认也为None
- return 可以返回多个值,多个值之间用逗号隔开,接收到的返回值会是一个元祖
- return 之后的代码将不会再继续执行
这个时候问题又来了,你是否已经和靓妹的感情已经达到了一个饱和程度,不需要再增进,而是改和靓仔增进之间的感情呢?
四、函数的参数
参数: 函数在调⽤的时候指定具体的⼀个变量的值, 就是参数。
def 函数名(参数列表):
函数体
这个时候我们在原有基础上添加上参数再试一下:
def add_friendship(user):
print("掏出手机")
print("启动探探APP")
print("挑选一个{0}".foramt(user))
print('问:"晚上交流一下感情可好?"')
print("OK!开始谈人生。")
return "今晚交流甚是欢悦"
ret = add_friendship("靓仔")
# 掏出手机
# 启动探探APP
# 挑选一个靓仔
# 问:"晚上交流一下感情可好?"
# OK!开始谈人生。
这个时候你已经实现了完全可以和任意对象去增进友谊了,我们只需要改动一下里面的参数即可,比如:
add_friendship("大妈")
add_friendship("大爷")
add_friendship("老师")
这个是你就可以尽情的去增进友谊了~~
关于参数:
形参:写在函数声明的位置的变量叫形参。形式上的⼀个完整, 表⽰这个函数需要接收xxx值
实参:在函数调⽤的时候给函数传递的值, 叫实参。 实际执⾏的时候给函数传递的信息, 表⽰给函数
xxx值传参:给函数传递信息的时候将实际参数交给形式参数的过程被称为传参。
def add_friendship(user): # 这里的user指的是形参 print("掏出手机") print("启动探探APP") print("挑选一个{0}".foramt(user)) print('问:"晚上交流一下感情可好?"') print("OK!开始谈人生。") return "今晚交流甚是欢悦" add_friendship("靓仔") # 这里的“靓仔”是实参 print("小伙子精力真好!") # 这里的“小伙子精力真好!”也是实参
参数的分类
首先我们先分析实参
位置参数
“你是否已经厌倦了探探上的增进友谊方式,没有关系,我们提供“位置参数”帮你解决问题,使你不仅可以在探探上增进友谊,还可以在陌陌上增进友谊!”
def add_friendship(platform ,user): # 这里的user指的是形参 print("掏出手机") print("启动{0}APP".format(platform)) print("挑选一个{0}".foramt(user)) print('问:"晚上交流一下感情可好?"') print("OK!开始谈人生。") return "今晚交流甚是欢悦" add_friendship("陌陌", "靓妹") # 掏出手机 # 启动陌陌APP # 挑选一个靓妹 # 问:"晚上交流一下感情可好?" # OK!开始谈人生。
在访问add_friendship()的时候, 我们按照位置的顺序分别把”陌陌”, “靓妹”在传参过程中赋给”platform”,”user”。 Python会默认按照位置把实参赋值到形参。 位置参数可以有多个。
关键字参数
“你是否经常因为增进友谊对象太多而忘记对象名字感到非常迷茫和尴尬,没有关系,我们提供“关键字参数”帮你解决这个问题!”
def add_friendship(liangzai ,liangmei, daye, dama, laoshi): print("今天和{0}增进了友谊!".format(liangzai)) print("今天和{0}增进了友谊!".format(liangmei)) print("今天和{0}增进了友谊!".format(daye)) print("今天和{0}增进了友谊!".format(dama)) add_friendship(liangmei="我是靓妹", liangzai="我是靓仔", daye="我是你大爷",laoshi="我是老师", dama="我是你大妈" ) # 今天和我是靓妹增进了友谊! # 今天和我是靓仔增进了友谊! # 今天和我是你大爷增进了友谊! # 今天和我是老师增进了友谊! # 今天和我是你大妈增进了友谊!
我们在传参的时候不需要记住每个参数的位置, 只要记住每个参数的名字就可以了。
混合参数
可以把上⾯两种参数混合着使⽤。 也就是说在调⽤函数的时候即可以给出位置参数, 也可以指定关键字参数。
def add_friendship(liangzai ,liangmei, laoshi): print("今天和{0}增进了友谊!".format(liangzai)) print("今天和{0}增进了友谊!".format(liangmei)) print("今天和{0}增进了友谊!".format(laoshi)) # 正确写法 add_friendship("我是靓仔", "我是老师", liangmei="我是靓妹") # 错误写法 add_friendship(liangmei="我是靓妹", "我是靓仔", laoshi="我是老师")
注意:在使⽤混合参数的时候, 关键字参数必须在位置参数后⾯
接下来我们分析形参:
位置参数
def eat(apple, banana): # 这就是位置参数 ...
默认值参数
在函数声明的时候, 就可以给出函数参数的默认值, 在调⽤的时候可以给出具体的值, 也可以不给值,不给值则使⽤默认值.。
def userinfo(name, age, gender="男"): print(name, age, gender) userinfo("dogfa", 18, "男") # dogfa 18 男 userinfo("class flower", 18, "女") # class flower 18 女 userinfo("djb", 18) # djb 18 男
注意: 必须先声明位置参数, 才能声明默认值参数。
动态参数
当要接收的参数个数是任意的,不确定个数的,这个时候我们就要用到动态参数了。
动态接收位置参数
def fruit(*args): print("I want to eat {0}".format(args)) # 多个参数传入接收到的是tuple类型数据 fruit("banana", "apple") # I want to eat ('banana', 'apple') fruit("banana", "apple", "orange") # I want to eat ('banana', 'apple', 'orange')
让我们来探究一下动态参数和位置参数的先后顺序关系:
# 先写动态参数,在写位置在参数 def fruit(*args, rice, vegetable): print("I want to eat {0} and {1} and {2}".format(args, rice, vegetable)) fruit("banana", "apple", "东北大米", "tomato") # TypeError: fruit() missing 2 required keyword-only arguments: 'rice' and 'vegetable' # 这时程序运⾏会报错。 因为前⾯传递进去的所有位置参数都被*args接收了, rice和vegetable永远接收不到参数 # 我们可以这样改写代码: fruit("banana", "apple", rice="东北大米",vegetable="tomato") # I want to eat ('banana', 'apple') and 东北大米 and tomato # 先写位置参数在写动态参数 def fruit(rice, vegetable, *args): print("I want to eat {0} and {1} and {2}".format(args, rice, vegetable)) fruit("东北大米", "tomato", "banana", "apple") # I want to eat ('banana', 'apple') and 东北大米 and tomato
**在参数位置编写*表⽰接收任意内容。动态接收参数的时候要注意: 动态参数必须在位置参数后⾯。**
那动态参数和默认值参数的位置关系呢:
# 先写动态参数,再写默认值参数 def fruit(*args, rice="东北大米"): print("I want to eat {0} and {1}".format(args, rice)) fruit("apple", "banana", "orange") # I want to eat ('apple', 'banana', 'orange') and 东北大米 # 这个时候我们发现默认值⽣效了. 这个时候如果不给出关键字传参. 那么你的默认值是永远都⽣效的. # 先写默认值参数,再写动态参数 def fruit(rice="东北大米", *args): print("I want to eat {0} and {1}".format(args, rice)) fruit("apple", "banana", "orange") # I want to eat ('banana', 'orange') and apple
总结顺序:位置参数, 动态参数*, 默认值参数
动态接收关键字参数
在Python中可以接收动态的位置参数, 但是*这种情况只能接收位置参数⽆法接收关键字参数.在Python中使⽤**来接收动态关键字参数
def func(**kwargs): print(kwargs) func(a=1, b=2, c=3) # {'a': 1, 'b': 2, 'c': 3} # 这个时候接收的是一个dict
注意: 在函数调⽤的时候, 如果先给出关键字参数, 则整个参数列表会报错。所以关键字参数必须在位置参数后⾯。 由于实参是这个顺序, 所以形参接收的时候也是这个顺序。 也就是说位置参数必须在关键字参数前⾯, 动态接收关键字参数也要在后⾯。
最终顺序:位置参数 > *args > 默认值参数 > **kwargs
如果想接收所有的参数
def func(*args, **kwargs): print(args, kwargs)
动态参数的传参
def func(*args): print(args) lst = [1, 2, 3, 4] func(*lst) # 打散列表传入 # (1, 2, 3, 4) temp = "你好啊靓仔!" func(*temp) # 也可以打散字符串传入 # ('你', '好', '啊', '靓', '仔', '!')
**在实参位置上给⼀个可迭代对象前⾯加个*表⽰把这个序列按顺序打散**
在形参的位置上的* 表⽰把接收到的参数组合成⼀个元组
如果是⼀个字典, 那么也可以打散, 不过需要⽤两个*,如下:
def func(**kwargs): print(kwargs) dic = {"name": "oldwang", "age": 18} func(**dic) # 打散字典传入 # {'name': 'oldwang', 'age': 18}
五、函数的注释
def add_friendship(platform, name):
'''
这⾥是函数的注释, 先写⼀下当前这个函数是⼲什么的, ⽐如我这个函数的功能就是增进友谊
:param platform:参数platform是什么意思
:param name:参数name是什么意思
:return:返回值是什么
'''
pass
六、命名空间
在Python解释器开始执⾏之后, 就会在内存中开辟⼀个空间, 每当遇到⼀个变量的时候,就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表⽰这个函数存在了, ⾄于函数内部的变量和逻辑, 解释器是不关⼼的。 也就是说⼀开始的时候函数只是加载进来, 仅此⽽已, 只有当函数被调⽤和访问的时候, 解释器才会根据函数内部声明的变量来进⾏开辟变量的内部空间。 随着函数执⾏完毕, 这些函数内部变量占⽤的空间也会随着函数执⾏完毕而被清空。
def func():
a = 10
print(a)
func()
print(a) # 这个时候a是不存在的
我们给存放名字和值的关系的空间起⼀个名字叫: 命名空间。我们的变量在存储的时候就是存储在这片空间中的。
命名空间分类:
- 全局命名空间:在函数外声明的变量都属于全局命名空间。
- 局部命名空间:在函数内部声明的变量属于局部命名空间。
- 内置命名空间:Python解释器默认提供的名字,比如 list,tuple,set等都属于内置命名空间。
加载顺序:
- 内置命名空间
- 全局命名空间
- 局部命名空间(函数执行的时候)
取值顺序:
- 局部命名空间
- 全局命名空间
- 内置命名空间
a = 10
def func():
a = 20
print(a)
func() # 20
print(a) # 10
作⽤域: 作⽤域就是作⽤范围, 按照⽣效范围来看分为 全局作⽤域和局部作⽤域。
全局作⽤域: 包含内置命名空间和全局命名空间。 在整个⽂件的任何位置都可以使⽤(遵循从上到下逐⾏执⾏)。
局部作⽤域: 在函数内部可以使⽤.。
- 全局作用域:全局命名空间 + 内置命名空间
- 局部作用域:局部命名空间
我们可以通过globals()来查看全局作用域中的变量和函数信息,也可以用locals()来查看局部作用域中的变量和函数信息。
def func():
a = 10
print(globals()) # 打印全局作⽤域中的内容
print(locals()) # 打印局部作用域中的内容
func()
# 注意:如果globas()和locals()都作用在全局命名空间,那么输出的内容是一样的。
七、函数的嵌套
def fun2():
print(222)
def fun3():
print(666)
print(444)
fun3()
print(888)
print(33)
fun2()
print(555)
# 33
# 222
# 444
# 666
# 888
# 555
八、关键字global和nonlocal
先分析global
# 首先我们看一段代码 a = 10 def func(): a = 20 print(a) func() # 20 print(a) # 10 # 该怎样才能在func()中改变全局变量a,使其打印都为20呢?这时候我们就该要引入global关键字了。 # 引入global后 a = 10 def func(): global a a = 20 print(a) func() # 20 print(a) # 20
在函数中引入global表示:不再使用局部命名空间中的变量了,而是使用全局命名空间中的变量。
接下来是nonlocal
a = 10 def func(): a = 20 def func1(): a = 30 print(a) func1() print(a) func() # 30 # 20 # 引入nonlocal a = 10 def func(): a = 20 def func1(): nonlocal a a = 30 print(a) func1() print(a) func() # 30 # 30
在函数中引入nonlocal表示:在局部作用域中调用父级命名空间中的变量(父级命名空间也必须是局部命名空间)
# 多层函数嵌套 a = 1 def fun_1(): a = 2 def fun_2(): nonlocal a a = 3 def fun_3(): a = 4 print(a) print(a) fun_3() print(a) print(a) fun_2() print(a) print(a) fun_1() print(a) # 1 # 2 # 3 # 4 # 3 # 3 # 1
九、函数名的运用
函数名是一个变量,但它是一个特殊的变量,如果直接打印的话得到的是这个函数的内存地址,我们需要在函数名后面加上()即可调用执行该函数。
函数名的内存地址
def func(): print("嘻嘻") print(func) # <function func at 0x000002AEDE852E18>
函数名可以赋值给其它变量
def func(): print("嘻嘻") a = func # 将函数名赋值给了变量a a() # 执行a() = 调用func()
函数名可以当做容器内的元素
def func(): print("嘻嘻") def func1(): print("嘻嘻") def func2(): print("嘻嘻") lst = [func, func1, func2] for i in lst: i() # 循环调用函数
函数名可以当做函数的参数
def func(): print("嘻嘻") def func2(fn): print("哈哈") fn() func2(func) # 哈哈 # 嘻嘻
函数名可以当做函数的返回值
def func2(): print("哈哈") def func3(): print("嘿嘿") return func3 func2()() # 执行func2()得到func3,func2()() = func3() # 哈哈 # 嘿嘿