2.1-python函数,作用域
python 函数
初始函数
函数的优势:
- 减少代码的重复性;
- 使代码可读性更高;
函数的结构与调用
函数的结构
def 函数名():
函数体
def 关键词开头,空格之后接函数名称和(),最后还有一个':';
def 是固定的,不能变,是定义函数的关键字;
空格,为了将def关键字和函数名分开;
函数名:函数名只能包含字符串,下划线和数字且不能以数字开头;起函数名要尽量简短,并具有可描述性;
考好:必须加的,可以传参使用;
函数的调用
使用函数名加()就可以调用了,写法:函数名(),这个时候函数的函数体就会被执行;
这个指令写多少次,函数里的代码就运行多少次;
函数的返回值
一个函数就是封装一个功能,这个功能一般都会有一个最终结果;比如写一个登陆函数,最终登陆成功与否就需要返回一个结果。
设置返回值,使用python的关键字:return;
return:函数中遇到return,此函数结束,不再继续执行;
函数的返回值返回给 函数名()这个整体,也就是这个执行者;
return也可以返回多个值,以元组的形式返回的;
总结:
-
遇到return,函数结束,return下面的(函数内)的代码不会执行;
-
return会给函数的执行者返回值;
- 如果return后面什么都不写,或者函数中没有return,则返回的结果是None
- 如果return后面写了一个值,返回给调用者这个值;
- 如果return后面写了多个结果,返回给调用者一个tuple(元组),调用者可以直接使用与uanzu的解构获取多个变量;
def date(): return '你好', '世界', '我是黑色利穆' a1, a2, a3 = date() print(a1, a2, a3) # 你好 世界 我是黑色利穆
函数的参数
函数是以功能为导向的;
函数的参数从两个角度划分:
- 形参:写在函数声明的位置的变量叫形参,形式上的一个完整,表示这个函数需要XX
- 实参:在函数调用的时候给函数传递的值,加实参,实际执行的时候给函数传递的信息,表示给函数XX
函数的传参就是函数将实际参数交给形参的过程;
def date(sex): # 函数定义时(参数)这个就是形参
print('设置筛选条件:性别: %s' %sex)
date('女') # 函数执行时(参数),这个就是实参
# 设置筛选条件:性别: 女
# 过程:代码运行到date('女'),开始执行此函数同时将字符串'女'这个数据传递给变量sex,然后执行函数中的代码
实参角度
位置参数
位置参数就只从左至右,实参与形参一一对应
def date(sex, age, hobby):
print('设置筛选条件:性别:%s, 年龄:%s, 爱好:%s' %(sex, age, hobby))
date('女', '25', '唱歌')
date('男', '25', '跳舞')
# 设置筛选条件:性别:女, 年龄:25, 爱好:唱歌
# 设置筛选条件:性别:男, 年龄:25, 爱好:跳舞
练习:
# 编写函数,给函数传递两个参数a,b。a,b相加,返回a参数和b参数相加的和
def f(a, b):
c = a + b
return c
print(f(10, 20))
# 30
# 编写函数,给函数传递两个参数a,b,比较a,b的大小,返回a,b中最大的那个数
def f(a, b):
if a > b:
return a
else:
return b
print(f(10,20))
# 20
# 三元运算符比较大小
def f(a, b):
c = a if a > b else b # 当a>b就把a赋值给c,否则就把b赋值给c
return c
print(f(10, 20))
# 20
关键字参数
如果函数在定义的时候参数非常多,位置参数就不好用了。所以可以使用关键字参数,就不需要记住繁琐的参数位置了。
def date(sex, age, hobby):
print('设置筛选条件:性别: %s,年龄:%s,爱好:%s' %(sex, age, hobby))
date(hobby='唱歌', sex='女', age='35')
# 设置筛选条件:性别: 女,年龄:35,爱好:唱歌
混合参数
混合参数,就是可以使用位置参数,也可以指定关键字参数;
ps:关键字参数一定要在位置参数后面;
def date(sex, age, hobby):
print('设置筛选条件:性别: %s,年龄:%s,爱好:%s' %(sex, age, hobby))
date('男', hobby='唱歌', age='35')
# 设置筛选条件:性别: 男,年龄:35,爱好:唱歌
总结:
在实参角度看,参数分为三种:1.位置参数;2.关键字参数;3.混合参数(位置参数必须在关键字参数前面);
形参角度
位置参数
形参的位置参数和实参的位置参数一样,按照从左至右,一一对应;
默认值参数
在函数声明的时候,就可以给出函数参数的默认值,默认值参数一般是这个参数使用率较高,才会设置默认值参数
def stu_info(name, age, sex='男'): # 录入学院信息,发现男生较多的时候,可以设置默认值
print('录入学生信息')
print(name, age, sex)
print('录入完毕')
stu_info('张三', 11)
在位置参数,才能声明关键字参数;
动态参数
动态参数分为两种:
- 动态接收位置参数 *args
- 动态接收关键字参数 **kwargs
动态接收位置参数:*args
def eat(*args):
print('今天我们吃的有:', args)
eat('蒸羊羔儿','蒸熊掌','蒸鹿尾儿','烧花鸭','烧雏鸡','烧子鹅')
# 今天我们吃的有: ('蒸羊羔儿', '蒸熊掌', '蒸鹿尾儿', '烧花鸭', '烧雏鸡', '烧子鹅')
args就是一个普通的形参,如果在args前加有个*,就具有了特殊意义。这样设置形参,这个形参会将实参所有的位置参数接收,放置在一个元组中,并将这个元组赋值给args这个形参;
练习:
# 传入函数中数量不定的int型数据,函数计算返回所有数的和并返回
def sum_max(*args):
n = 0
for i in args:
n += i
return n
print(sum_max(1, 2, 3, 4, 5, 6, 7, 8, 9))
# 45
动态接收关键字参数:**kwargs
实参角度有位置参数和关键字参数两种,*args接受所有位置参数,**kwargs接收所有关键字参数。接收所有关键字参数,然后将其转化成一个字典赋值给kwargs这个参数;
def func(**kwargs):
print(kwargs)
func(name='黑色利穆', sex='男')
# {'name': '黑色利穆', 'sex': '男'}
动态参数的万能写法:
def func(*args, **kwargs):
print(args)
print(kwargs)
func('蒸羊羔儿','蒸熊掌','蒸鹿尾儿', name='黑色利穆', sex='男')
# ('蒸羊羔儿', '蒸熊掌', '蒸鹿尾儿')
# {'name': '黑色利穆', 'sex': '男'}
*的魔性用法
*的用法:
- 函数中分为打散和聚合;
- 函数外可以处理剩余的元素;
函数的打散和聚合
聚合:
*args将多个位置参数的实参,转成元组;**kwargs将多个关键词参数的实参,转成字典;起到的就是聚合作用;
打散:
s = 'HSLM'
l = [1, 2, 3, 4]
tu = ('黑色', '利穆')
def func(*args):
print(args)
func(s, l, tu)
# ('HSLM', [1, 2, 3, 4], ('黑色', '利穆'))
func(*s, *l, *tu)
# ('H', 'S', 'L', 'M', 1, 2, 3, 4, '黑色', '利穆')
dic1 = {'name': '黑色利穆', 'age': 25}
dic2 = {'hobby': '唱歌', 'sex': '男'}
def func(**kwargs):
print(kwargs)
func(**dic1, **dic2)
# {'name': '黑色利穆', 'age': 25, 'hobby': '唱歌', 'sex': '男'}
*处理剩下的元素
# 分别赋值
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]
形参的第四种参数:仅限关键字参数
位置:放在*args后面,kwargs前面,默认参数的位置。与默认参数的前后顺序无所谓,只接受关键字传的参数:
def func(a, b, *args, c):
print(a, b)
print(args)
print(c)
func(1, 2, 3, 4, c=5)
# 1 2
# (3, 4)
# 5
形参的顺序
位置参数,*args,默认参数,仅限关键字参数, **kwargs
练习:
'''
def foo(a,b,*args,c,sex=None,**kwargs):
print(a,b)
print(c)
print(sex)
print(args)
print(kwargs)
# foo(1,2,3,4,c=6)
# foo(1,2,sex='男',name='alex',hobby='黑色利穆')
# foo(1,2,3,4,name='黑色利穆',sex='男')
# foo(1,2,c=18)
# foo(2, 3, [1, 2, 3],c=13,hobby='喝茶')
# foo(*[1, 2, 3, 4],**{'name':'黑色','c':12,'sex':'女'})
'''
名称空间,作用域
名称空间
- 全局命名空间 :直接在py文件中,函数外声明的变量都输全局命名空间;
- 局部命名空间:在函数中声明的变量会放在局部命名空间;
- 内置命名空间:存放python解释器为我们提供的名字,list,tuple,str,int这些都是内置命名空间;
加载顺序
内置命名空间(程序运行伊始加载) -- 全局命名空间(程序运行中:从上到下加载) -- 局部命名空间(程序运行中:调用时才加载)
取值顺序
局部名称空间 -- 全局名称空间 -- 内置名称空间
作用域
作用域就是作用范围,按照生效范围分为全局作用域和局部作用域;
全局作用域:包含内置命名空间和全局命名空间,在整个文件的任何位置都可以使用(遵循从上到下逐行执行);
局部作用域:在函数内部可以使用;
作用域命名空间:
- 全局作用域:全局命名空间, 内置命名空间
- 局部作用域:局部命名空间
内置函数globals(), locals()
globals():字典的形式返回全局作用域所有的变量对应关系;
locals():字典的形式返回当前作用域的变量的对应关系;
# 在全局作用域下打印
a = 2
b = 3
print(globals())
print(locals())
# 在局部作用域打印
a = 2
b = 3
def foo():
c = 3
print(globals())
print(locals())
foo()
高阶函数(函数的嵌套)
只要遇见函数名+()就是函数的调用,如果没有就不是函数的调用;
# 例3:
def fun2():
print(2)
def fun3():
print(6)
print(4)
fun3()
print(8)
print(3)
fun2()
print(5)# 例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 fun2():
print(2)
def fun3():
print(6)
print(4)
fun3()
print(8)
print(3)
fun2()
print(5)
关键字:global,nonlocal
global
局部作用域对全局作用域的变量(此变量只能是不可变的数据类型)只能引用,不能改变;需要改变时就要用到关键字global:
global作用:
- 声明一个全局变量
- 局部作用域对全局作用域的全局变量进行修改时,用global
count = 1
def search():
global count
count = 2
search()
print(count)
# 2
def func():
global a
a = 3
func()
print(a)
# 3
nonlocal
局部作用域想对父级作用域的变量进行改变时,需要用到nonlocal;
nonlocal作用:
- 不能改变全局变量;
- 在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的那层及下变量全部发生改变;
def add_b():
b = 33
def do_global():
b = 10
print(b)
def dd_nonlocal():
nonlocal b
b = b + 10
print(b)
dd_nonlocal()
print(b)
do_global()
print(b)
add_b()
# 10
# 20
# 20
# 33