Python——函数&作用域
我们前面学的都是面向过程式的编程(代码从上到下写,并运行),而函数式编程是将繁杂的代码通过整理特性进行规整。像图书馆一样,将小说、教学、外文等书籍进行分类。让编程人员或看代码人员很容易的查看该段代码的特性。并且可以重复的利用该段代码进行相同的操作、像图书馆书是公用的一样,谁都可以去看,这样又能便于观察又能重复使用,是日后主要使用的技术。
def test(): #设置一个函数名称
#test #注释函数作用
print('函数式编程' ) #函数程序处理逻辑
return 0 #定义返回值,如果没有定义将返回None
test() #调用
函数:
1. 可以在调用时,在括号内放入参数,里面有(位置参数,关键字参数,混合参数,默认参数,汇总参数)
2. 位置参数必须要在关键字参数前面。
3. 函数传的参数赋值中,只复制引用而不复制对象。
位置参数:
将参数一一对应,并传入到函数中:
def func(a, b, c):
print(a, c, b)
func(1, 2, 3) #将1=>a,2=>b,3=>c
关键字参数:
将特定的参数以赋值形式对应起来。
def func(a, b, c):
print (a, c, b)
func(a=1, b=2, c=8)
混合参数(位置参数与关键字参数)
不能写在位置参数的前面:
def func(a, b, c):
print (a, c, b)
test(3, b= 2, c=1 ) √
test(c=1, 2) X
默认参数:
调用函数的时候,默认参数非必须传递。
如果再没有定义其他,就以默认的来。
在定义默认值时:不可变类型随便传,但可变类型需要注意:
def test(x, y=1):
print(y)
test(2)
汇总参数:
1. *args:接收N个位置参数,转换成元组的形式。
def test1(x, *args):
print(x)
print(args)
test1(1, 2, 3, 4, 5, 6) & test1(1, *[2, 3, 4, 5, 6]) #这里的*后面的列表是将列表内容提取并重新赋值给元组,以元组形式输出。

同时允许将列表项逐个传递给函数,无论列表项的数量是多少。
user_list = [1, 2, 3, 4, 5, 6,]
print(user_list)
print(*user_list,111)
[1, 2, 3, 4, 5, 6]
1 2 3 4 5 6 111
2. **kwargs:把N个关键字参数,转换成字典的方式。
def test1(x,**kwargs):
print(x)
print(kwargs)
# test1(1,name='3',age=20)
test1(2, name = 'xuan',age=9)
test1(1, **{'name':"xuan", 'age':20}) #将字典内容提取,在重新赋值给函数内的kwargs
2
{'name':'xuan', 'age':9 }
1
{'name':'xuan', 'age':20 }
通过sep关键字参数来将里面的数值进行拼接起来
默认是使用空字符串进行拼接
print(1, 2, 3, sep='=')
joinPrint = {'sep': '=='} #性质是一样的
print(1, 2, 3, **joinPrint)
作用域:
在Python中,一个函数就是一个作用域,而所有的函数都挂靠在.py文件的总作用域中。
在这里写的代码分为:全局作用域和局部作用域
1. 全局作用域
- 要使用全大写形式定义变量名称,为了将局部变量做标识。
- 局部作用域全部挂靠在全局作用域内
NAME = 'a1'
def func():
name = 'a2'
print (name)
print (NAME)
func()
2. 局部作用域:
- 局部作用域可以调用全局作用域。局部和局部,全局和局部不能调用。
- 局部作用域之间无法调用,可以使用父作用域。
def change_name(name): #一个函数叫做一个作用域, print ( 'before change' ,name) name = 'XB' print ( 'after change' ,name) name = 'xb' change_name(name) print (name)
global:
在局部作用域中,进行全局作用域内容的查找,并可以修改。(只能修改可变类型,不可变类型为局部内重新赋值)
NAME = 'a1'
def func():
global NAME
NAME = 'bbb'
func()
print (NAME)
nonlocal:
在子局部作用域中,进行对父级局部作用域的内容查找不更改全局作用域内容,并可以修改。(同样修改可变类型,不可变类型为局部内重新赋值)
NAME = 'a1'
def func():
NAME = '111'
def func2():
nonlocal NAME
NAME = 'bbb'
func2()
print (NAME)
func()
print (NAME)
返回值:
在函数中使用return来进行函数这个子作用域的返回值的关键字。其中:
1. 默认如果没有return,但定义了取值变量的话,默认为None。
def papa():
print ( '111' )
a = papa() #a就是None
2. 如果只有一个返回值,那么就返回相应的数值和数据类型。如果有多个返回值,那么将返回一个元组。
def papa():
dic = { 'k1' : 'v1' }
return dic #将返回字典类型
return { 'k1' : 'v1' },{ 'k1' : 'v1' },{ 'k1' : 'v1' } #将返回元组类型
a = papa()
print (a)
3. 返回值同样可以返回另一个函数的内存对象地址
def a():
print ( '111' )
def papa():
return a #返回一个内存对象地址,返回后可以直接运行。
b = papa()
b()lam
lambda
用于表示简单的函数时,可以使用lambda来进行,其中需要注意的:
1. 只能用一行来表示lambda
2. 只能用参数传的值,而不能自己定义一个变量。
使用lambda默认就会有一个return。
a = lambda : 1 + 1 #将1+1的结果返回到a中。 print (a())
lambda可以搭配三元运算使用:
a = lambda : 1 if 1 + 1 = = 2 else 2 #判断如果1+1等于2的话,那么返回1,否则返回2 print (a())
其他:
lis = lambda : [ i for i in range ( 10 ) if i % 3 = = 0 ] #循环1-10,判断其中哪些为3整除,添加到列表中,生成一个lambda。 print (lis())
闭包:
闭包是嵌套在函数中的函数,而闭包必须是内层函数对外层函数的变量(非全局变量)的引用。
为函数创建一个区域(内部变量供自己使用)为以后执行提供数据。
#实例
li = []
def func(new_value):
li.append(new_value)
total = sum (li)
return total / len (li)
print (func( 1000 ))
print (func( 2000 ))
print (func( 5000 ))
#那么问题来了,li是全局变量,我可以在全局随意更改。
li = []
def func(new_value):
li.append(new_value)
total = sum (li)
return total / len (li)
print (func( 1000 ))
print (func( 2000 ))
li.append( 120301 )
print (func( 5000 ))
#得出来的结果就大不相同了。如何有解决办法,那就是把li放到函数的小作用域里。
def func(new_value):
li = []
li.append(new_value)
total = sum (li)
return total / len (li)
print (func( 1000 ))
print (func( 2000 ))
print (func( 5000 ))
#这样一来,每次运行函数,就会新生成一个新的列表出来。那么值就没有变化了。接下来如何解决这个问题。闭包就来了。
def func():
li = []
def func1(new_value):
li.append(new_value)
total = sum (li)
return total / len (li)
return func1
func = func()
print (func( 1000 ))
print (func( 2000 ))
print (func( 5000 ))
#这样一来,即不会出现全局模式更改的问题,也不会担心列表重新生成的问题。这就是比好。
其中,下面一块区域叫做闭包:
li = []
def func1(new_value):
li.append(new_value)
total = sum (li)
return total / len (li)
我们可以通过查看函数内是否有自由变量来侧面证明是不是闭包。
def func():
li = []
def func1(new_value):
li.append(new_value)
total = sum (li)
return total / len (li)
return func1
func = func()
print (func.__code__.co_freevars) #查看函数的自由变量
#('li',)
print (func.__code__.co_varnames) #查看函数的局部变量
#('new_value', 'total')
函数需要注意的点:
1. 根据数据类型的不同,有些是有返回值的,有些是没有返回值的。
lis = []
def a():
return lis.append( '111' )
b = a()
print (b) #由于列表形式的添加是没有返回值的,所以返回的是None
lis = '123'
def a():
return lis + '321'
b = a()
print (b) #由于字符串是有返回值的,所以返回的是所需值
2. 需要判断是传入函数内存地址还是函数的return的值。
def func():
print ( '1111' )
return 0
lis = [func,func,func] #这里传入的是函数的内存地址。不是执行结果。
for item in lis:
print (item) #打印的是内存地址
'''
'''
def func():
return 0
lis = [func(),func(),func()] #这里传入的是运行完func的返回值
for item in lis:
print (item) #打印的是0
'''
0
0
0
'''
3. 闭包问题
def func(name):
v = lambda x:x + name
return v
v1 = func( '武沛齐' )
v2 = func( 'alex' )
v3 = v1( '银角' )
v4 = v2( '金角' )
print (v1,v2,v3,v4)
result = []
for i in range ( 10 ):
func = lambda : i # 注意:函数不执行,内部代码不会执行。
result.append(func)
print (i)
print (result)
v1 = result[ 0 ]()
v2 = result[ 9 ]()
print (v1,v2)
def func(num):
def inner():
print (num)
return inner
result = []
for i in range ( 10 ):
f = func(i)
result.append(f)
print (i)
print (result)
v1 = result[ 0 ]()
v2 = result[ 9 ]()
print (v1,v2)
4. 实参与形参
传参的类型是否为可变类型,如果可变类型,那么实参也会一起改变,如果是不可变类型,那么就会重新创建一个方法内部变量。
###########传入不可变类型############
def func(a):
a + = 1
print ( id (a))
a = 7
func(a)
print ( id (a))
print (a)
#判断ID输出是否一样,a输出的结果?
############传入可变类型###################
def func(a):
a[ 0 ] = 8
print ( id (a))
a = [ 1 , 2 , 3 ]
func(a)
print ( id (a))
print (a)
#判断ID输出是否一样,a输出的结果?
函数中的那些坑:
1. 函数中有定义一个空列表作为形参:
def a(b,lis=[])
问题:在没有传入新的列表当做实参时,将会使用定义好的空列表,那么将会导致多个没有定义的实参重复调用该空列表。这将导致数据内容不一致的问题。
def b(a,lis = []):
for i in range (a):
lis.append(i * i)
print (lis)
b( 3 )
b( 3 )
b( 3 )
'''
[0, 1, 4]
[0, 1, 4, 0, 1, 4]
[0, 1, 4, 0, 1, 4, 0, 1, 4]
'''
可以传参时传入一个自己的空列表,那么就将使用自己传入的空列表。
def b(a,lis = []):
for i in range (a):
lis.append(i * i)
print (lis)
b( 3 ,[])
b( 3 ,[])
b( 3 ,[])
'''
[0, 1, 4]
[0, 1, 4]
[0, 1, 4]
'''
如何解决这类问题,要么就删除空列表,要么就进行判断。
def b(a,lis = []):
if lis:
lis = []
for i in range (a):
lis.append(i * i)
print (lis)
b( 3 ,[])
b( 3 ,[])
b( 3 )
b( 3 )
b( 3 )
'''
[0, 1, 4]
[0, 1, 4]
[0, 1, 4]
'''
编写高效的函数
- 起一个容易被理解的函数名
- 函数内代码行数最少30行,最多200行
- 传参最好保持0~3个,最好不要超过6个。
- 用*来进行列表的输出
- 用**来进行拼接
- 编写纯函数的最常见的方法是避免在函数内部使用全局变量,并确保不与文件、互联网、系统时钟、随机数或其他外部资源交互。
- 函数具备其他任何对象都有的功能,可以把函数存储在变量中,作为参数传递,或者把它作为返回值使用。
- 应尽量让返回值的数据类型保持不变。
- 不要写返回值为None
指定返回参数
类型提示使用冒号来分割参数和变量的名称与类型。对于返回值,类型提示使用(->)分割def语句的闭合括号和类型。
# 函数类型提示。 提示的参数数据类型为int,返回的参数为str
def descNumber(number:int) -> str:
if number % 2 == 1:
return '1111'
elif number == 66:
return '2222'
else:
return '6666'
num : int = 44
print(descNumber(num))

浙公网安备 33010602011771号