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')
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章