9.函数详解-python基础知识
匿名函数lambda函数将在后续文章中讲解,事实上,如果把它看做一个表达式也是可以理解的。
定义,调用
函数通过def
定义,执行时会创建函数对象,并将其赋值给函数名,通过return
返回值,如果执行控制流执行到函数体末尾没有发现可执行return
的时候,则会返回None
。
-
定义:
def functionname(arg1,...,argn): 函数语句 return 返回值
-
调用:
通过函数名调用
-
多态:
python中的函数是多态的,不必定义传入参数的类型
作用域
访问:LGBE原则
- L: local,局部作用域,即函数,类中定义的变量。
- E: enclosing,父级函数的局部作用域,即此函数的上级函数的局部作用域。
- G: globa,全局作用域
- B: build-in,内置作用域,如:int,max函数等,使用
builtins
函数查看全部
访问优先级顺序为:局部作用域(L)>父级函数作用域(E)>全局作用域(G)>系统模块(B)
来看一个例子:
x = 99
def func1(y):
z = x+y
return z
print(func1(1))
>>>100
x = 99
def func2():
x = 100
func2()
print(x)
>>>99
前面说过,由于函数内的参数在函数结束时会被回收,再func1中,z是在函数中定义的,y是传入的参数,x先访问局部作用域没找到,在访问到全局作用域,找到了x=99,然后计算返回了z
而在func2中,函数内的x=100不是对全局变量x进行了赋值更改,而是重新定义了局部作用域的变量x,所以在全局作用域输出x还是等于100,要实现赋值变更,需要用到下面讲到的global
函数
global,nonlocal
global
将变量声明为全局变量,在整个模块文件中都可以使用,global需要在函数内部声明,在外部声明则无效
#在局部作用域func1中调用全局变量x
x = 99
def fun1():
global x
x = 100
fun1()
print(x)
>>>100
nonlocal
将变量声明为父级函数的局部变量
x = 99
def fun1():
global x#声明x是全局变量的x
x = 100#实现了对x的赋值修改
y = 100#声明fun1的局部变量y
def fun2():
nonlocal y#声明y是父级fun1的局部变量
y = 99#实现对y的修改
print('func2,y:',y)
fun2()
fun1()
print('main,x:',x)
>>> func2,y: 99
main,x: 100
如果父级没有定义该变量,则会报错,即使父级引入全局变量也不行:
x = 99
y = 100
def fun1():
global x ,y
x = 100
# y = 100 #父级未定义y
def fun2():
nonlocal y #报错,y未定义
y = 99
print('func2,y:',y)
fun2()
fun1()
print('main,x:',x)
>>> File "E:/Python_project/Django/QShop/QShop/__init__.py", line 7
nonlocal y
^
SyntaxError: no binding for nonlocal 'y' found
*循环变量与默认参数
默认参数x=x,这样既完成了从父级循环导入变量,又不会像global一样改变原有变量值:
def fun1():
x=10
def fun2(x=x):
x=20
print('fun2',x)
fun2()
print('fun1',x)
fun1()
再来看一个例子,如果需要使用循环来定义一些嵌套函数,需要使用默认参数来保存状态:
lambda也是定义了函数,函数makeAction可以用来计算次方
def makeActions():
acts = []
for i in range(5):
acts.append(lambda x:i**x)
return acts
acts = makeActions()
print(acts[0])
>>><function makeActions.<locals>.<lambda> at 0x000001E56F172AF8>
但是上面的函数,传入底数2
acts[0](2) >>>16
acts[1](2) >>>16
acts[2](2) >>>16
acts[3](2) >>>16
acts[4](2) >>>16
所有的都是2的四次方,说明参数i都是4,这时就需要i保持默认值:
def makeActions():
acts = []
for i in range(5):
acts.append(lambda x,i=i:i**x)
return acts
acts = makeActions()
acts[0](2) >>>0
acts[1](2) >>>1
acts[2](2) >>>4
acts[3](2) >>>9
acts[4](2) >>>16
参数
如何传递
参数实际上传递的引用,将对象地址指向函数运行时新建的变量上
可变对象此时对其操作会改变对象,所有引用对象的变量会改变值
l = [1,2,3]
def change(object):
object = object.append(4)
change(l)
print(l)
>>>[1, 2, 3, 4]
不可变对象的操作其实是新建对象来指向,所以不会改变原对象的值
如果仔细阅读过前面的可变对象与不可变对象的原理,这里是很容易理解的。
s = 'abc'
def change(object):
object = object+'de'
change(s)
print(s)
>>> abc
参数类型
-
匹配规则:从左往右进行匹配,常用参数必须放在前面
1.通过位置分配常用参数
2.通过匹配变量名分配关键字参数
3.其他额外的非关键字参数匹配到*name元组中
4.其他额外的关键字参数分配到**name字典中 -
常用参数:
def f(a,b,c): print(a,b,c) f(1,2,3)>>>1 2 3
-
默认参数:为没有传入值的参数定义缺省值
在函数定义时执行def f(a=1,b=2,c=3): print(a,b,c) f(1)>>>1 2 3
-
关键字参数:通过参数名进行匹配
在函数调用时使用def f(a,b,c): print(a,b,c) name = 'x';age = 14;city = 'BJ' f(a=age,b=city,c=name)>>>14 BJ x
-
收集参数:在函数定义中,使用
*
用元组收集任意多基于位置的参数,使用**
用字典收集任意多关键字参数,并且收集参数是可以一起使用的。def f(a,b,c,*d): print(a,b,c,d) f(1,2,3,4,5,6,7) >>>1 2 3 (4, 5, 6, 7)
def f(a,b,**d): print(a,b,d) f(1,2,x=3,y=4) >>>1 2 {'x': 3, 'y': 4}
-
可变解包参数:与收集参数刚好相反,解包参数是在函数调用时,使用
*
解包基于位置的参数,使用**
解包关键字参数。def f(a,c,x,y): print(x,y,a,c) D={'x': 3, 'y': 4} f(*(1,2),**D) >>>3 4 1 2
再次需要我们注意的是,不管是函数定义还是参数解包赋值的时候,一定要注意参数的顺序,常用参数放前面,关键字参数放后面,不然会匹配不到常用参数报错。
-
keyword-only参数:
在一个星号参数后、或者一个可变位置参数后的形参
函数调用时必须使用关键字参数传参
调用时如果没有默认值,则必须传递实参,否则将抛出TypeError缺少keyword-only参数异常def func(*args, x=1, y, **kwargs): print(x) print(y) print(args) print(kwargs) func(3, 5, x=3, y=5, b='KeithTt')