(一)python函数变量作用域
(1)作用域的分类
Python中,程序的变量并不是在任何位置都可以访问的,具体从哪个位置可以访问,取决于这个变量是在哪里赋值的。
变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。
Python的作用域好一共有4种,分别是:
- L(Local) 局部作用域
- E(Enclosing)闭包函数外的函数中
- G(Global)全局作用域
- B(Built-in)内建作用域
以上作用域范围按L->E->G->B的规则查找,即:在局部找不到,变回去局部外的局部找(例如闭包),再找不到就回去全局找,再者去内建找。
建议全局变量名以g_开头,例如:g_sum
(2)作用域的界定
在python中,以下定义都会产生新的作用域:
- 模块 module
- 类 class
- 函数 def lambda
而以下代码块则不会引入新的作用域:
- if…elif….else
- try/except
- for/while
在以上这些语句中定义的变量,外部都可以访问
例如:
#sum_1在循环体内定义,但是从循环体外也可以访问,for不影响变量作用域,不会产生新的作用域for i in range(0,101):if i == 0:sum_1 = 0sum_1 += iprint("sum1=%d" % sum_1)
运行结果为:
sum1=5050
关于函数影响变量作用域的例子,例如:
#这里i定义在函数体内部,则不能在函数体外访问,如果访问会报错def sum_1(i):if i > 0:return i + sum_1(i-1)else:return 0print(sum_1(100))print("i=%d" % i )
运行后,报错信息为:
NameError: name 'i' is not defined
(3)局部变量和全局变量
局部变量定义在函数体内部,只拥有一个局部作用域,而全局变量定义在函数体外部,拥有全局作用域。
局部变量只能在其被声明的函数体内部访问,而全局变量可以在整个程序范围内被访问。调用函数时,所有在函数体内声明的变量名称都将被加入到作用域中。如:
#这里在set_name和get_name重定义的name都属于局部变量,只能从函数体内部访问def set_name():name = "zhangsan"return namedef get_name(name):print(name)nm = set_name()print("nm={}".format(nm))get_name("susan")
运行结果为
nm=zhangsansusan
全局变量从函数体外定义,可以在不同的函数中使用,属于全局型变量,如:
#函数体外定义的name变量,为全局变量,无论从函数内访问,还是函数外访问,都是一样的name = "zhangsan"def get_name():print(name)def get_name2():print(name)get_name()get_name2()print(name)
结果都是
zhangsanzhangsanzhangsan
(4)修改全局变量的值
全局变量的值可以从函数体内,也可以从函数体外修改,如:
#定义了一个全局变量age = 20def change_age():#函数体内重新定义了一个age,这个是局部变量,不同于全局变量age=25print("函数体内的age=%d" % age)change_age()print("全局变量age=%d" % age)
运行结果为
函数体内的age=25全局变量age=20
由此可见全局变量并没有被修改
如果要在函数体内重新给全局变量赋值,则需要使用global关键字来重新定义变量的作用域
例如:
age = 20def change_age():#global关键字指定其后的age为全局变量,这样在函数体内可以重新给全局变量赋值global ageage=25print("函数体内的age=%d" % age)change_age()print("全局变量age=%d" % age)
运行结果为:
函数体内的age=25全局变量age=25
由此,我们可以发现,在函数体内通过指定变量为全局变量,可以重新给全局变量赋值
(5)全局变量定义的位置
g_num1=100def print_global_num():print("g_num1:%d" % g_num1)print("g_num2:%d" % g_num2)print("g_num3:%d" % g_num3)g_num2 = 200print_global_num()g_num3 = 300
运行得:
g_num1:100g_num2:200NameError: name 'g_num3' is not defined
全局变量必须在被使用之前定义
(6)字典、列表作为全局变量,在函数体内修改这种类型的全局变量中的元素
字典和列表被定义为全局变量之后,可以直接在函数内修改字典和列表中的元素,例如:
g_num_list = [1, 2, 3]g_info_dict = {"anme": "zhangsan", "age": 20}def update_info():#增加一个元素4g_num_list.append(4)#增加一个键值对:gender和maleg_info_dict["gender"]="male"update_info()print(g_num_list)print(g_info_dict)
运行结果为:
[1, 2, 3, 4]{'anme': 'zhangsan', 'age': 20, 'gender': 'male'}
(7)缺省参数
定义函数形参的时候,可以指定缺省值,形如:
def fun(x=10,y=20)
举例如下:
#定义函数x_y_sum,并指定形参y的缺省值为20def x_y_sum(x, y = 20):print("x=%d" % x)print("y=%d" % y)return x + yrs1 = x_y_sum(10, 20)print("rs1=", rs1)#调用的时候形参y对应的实参缺省,则取默认值rs2 = x_y_sum(10)print("rs2=", rs2)#定义的时候形参y对应的实参指定为30,则y取值为指定的30rs3 = x_y_sum(10, 30)print("rs3=", rs3)
运行结果为:
x=10y=20rs1= 30x=10y=20rs2= 30x=10y=30rs3= 40
注意:定义有默认值的形参的时候,必须把默认值放到最后面,否则形参和实参结合的时候会发生错误,以下的定义就是错误的
def x_y_sum(y=20,x):return x+y
(8)命名参数
命名函数,即在调用函数的时候,指定名称的实参,实参名称与函数定义时的形参要相同,但是顺序可以不同
例如:
def x_y_sum(x=10,y=20):return x+y#用命名参数调用,指定调用实参要结合的形参名rs1 = x_y_sum(y=50,x=100)#用命名参数调用,只指定了x,y没指定,缺省rs2 = x_y_sum(x=15)#x,y都缺省rs3 = x_y_sum()print("rs1=%d" % rs1)print("rs2=%d" % rs2)print("rs3=%d" % rs3)
运行结果为
rs1=150rs2=35rs3=30
(二)不定长参数
(1)用元组接收不定长的参数
1)格式
很多时候,一个函数调用的参数事先我们并不知道,这个时候就可以使用不定长参数
第一种不定长参数是使用元组接收参数,格式如下
def func(x,y,z...,*args):statements
2)不定长参数举例
#定义一个函数,函数中包含了接收不定长参数的*args,函数内对不定长参数进行处理def any_num_sum(x, y=10, *args):print("args={}".format(args))rs = x + yif len(args) > 0:for arg in args:rs += argreturn rsrss = any_num_sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)print("rs={:d}".format(rss))
运行得:
args=(3, 4, 5, 6, 7, 8, 9, 10)rs=55
3)改变不定长参数的位置
不定长参数可以放到命名参数的前面,这要求命名参数在与实参结合的时候,一定要指定实参的名称,否则匹配会发生错误
例如:
def any_num_sum2(x, *args, y=10):print("args={}".format(args))print("x={}".format(x))print("y={}".format(y))rs = x + yif len(args) > 0:for arg in args:rs += argreturn rs#调用的时候只有一个实参rs1 = any_num_sum2(20)#指定了三个实参,一个按位置结合,一个不定长参数,一个命名参数rs2 = any_num_sum2(20, 30, y=100)#最后一个命名参数同形参y结合rs3 = any_num_sum2(20, 30, 40, 50, 60, 70, y=100)print(rs1)print(rs2)print(rs3)
运行结果为:
args=()x=20y=10args=(30,)x=20y=100args=(30, 40, 50, 60, 70)x=20y=10030150370
(2)用字典接收不定长的参数
用字典接收的参数,都是key-value键值对的形式,key为变量名,value为变量的值。
其基本形式如下:
def function([formal_args,]**kvargs)
使用这种形式,在调用的时候需用变量名=值的形式赋值。
举例如下:
#使用列表和字典格式接收的时候,形参列表中*argu在前,**kwargu在后def social_insurance_comp(basic_money, *staff, **proportion):print(proportion)print(staff)e_money = basic_money * proportion["e"]m_money = basic_money * proportion["m"]a_money = basic_money * proportion["a"]total_money = e_money + m_money + a_moneyfor item in staff:print("item=%s" % item)return total_money#当实参和形参结合的时候,列表实参变量在前,字典实参变量在后,顺序不能发生错误sum_1 = social_insurance_comp(1000, "zhangsan", "lisi", "wangwu", e=0.2, m=0.1, a=0.05)print("sum={}".format(sum_1))
关于拆包
举例如下:
def salary_comp(basic_money, *other_money, **proportion):print("缴费基数:{}".format(basic_money))print("其他工资:{}".format(other_money))print("比例:{}".format(proportion))other_money = (500, 200, 100, 1000)proportion_dict = {"e": 0.2, "m": 0.1, "a": 0.12}#在这种调用形式下,other_money和proportion_dict被当做一个列表参数提提供给了*other_money,则结果只能是列表和词典作为一个元组传入salary_comp(8000, other_money, proportion_dict)
运行结果如下:
缴费基数:8000其他工资:((500, 200, 100, 1000), {'e': 0.2, 'm': 0.1, 'a': 0.12})比例:{}
正确,采用拆包方式传递参数,应该如下:
def salary_comp(basic_money, *other_money, **proportion):print("缴费基数:{}".format(basic_money))print("其他工资:{}".format(other_money))print("比例:{}".format(proportion))other_money = (500, 200, 100, 1000)proportion_dict = {"e": 0.2, "m": 0.1, "a": 0.12}#调用的时候,列表前加*,词典前加**,则可以实现拆包调用salary_comp(8000, *other_money, **proportion_dict)
(三)递归函数
递归函数,是调用其自身的函数,其典型应用为求阶乘,例如:
def recursive(num):if num > 1:return num * recursive(num-1)else:return 1print('{}'.format(recursive(4)))
运行结果为
24
(四)匿名函数
lamba匿名函数,其实质为没有名称的函数,定义格式如下:
lambda 形参1,形参2:返回值表达式等同于:def 函数名(形参1,形参2):返回值表达式
举例如下:
(1)匿名函数作为参数传入到自定义函数中
def x_y_comp(x,y,func):rs = func(x,y)print(rs)#在传入参数的时候,指定函数的功能为加法x_y_comp(3, 5, lambda x,y:x+y)print("---")#在传入参数的时候,指定函数的功能为减法x_y_comp(10, 6, lambda x, y:x-y)
运行结果为:
8---4
(2)匿名函数作为python内置函数的参数使用
例如:
#定义一个字典列表user_infos = [{"name":"zhangsan", "age":20}, {"name": "lisi", "age":30}, {"name":"wangwu", "age": 18}]#按指定关键字排序,注意:通过匿名函数去出每个字典的age成员,参与排序,字典本身不能排序user_infos.sort(key=lambda info:info["age"], reverse=True)print(user_infos)
运行结果为:
[{'name': 'lisi', 'age': 30}, {'name': 'zhangsan', 'age': 20}, {'name': 'wangwu', 'age': 18}]
