011 Python中的 函数 ——理论篇

一、什么是函数

是可以重复执行的语句块,可以重复调用

作用

用于封装语句块,提高代码的重用性

用于定义/创建用户级别的函数

语法

def 函数名(形参列表):

语句块

创建一个函数,把函数内的语句块打包为一个函数,用函数名绑定

说明

1.函数名就是语句块的名称

2.函数名的命名规则与变量名相同(函数名必须是标识符)

3.函数名是一个变量(绑定对象,绑定函数)

4.函数有自己名字空间,在函数外部不可以访问函数内部的变量,在函数内部可以访问函数外部的变量,要让函数处理外部数据需要用参数给函数传入一些数据

5.参数列表可以为空

6.语句部分不能为空,如果为空需要用pass语句填充

函数调用

函数名(实际调用传递参数)

说明

1.函数调用是一个表达式

2.如果没有return语句,函数执行完毕后返回None对象

3.如果函数需要返回其它的对象需要用到return语句

问题

def mysum(a,b)

print(a+b)

r = mysum(100,200)

print(r)

#请问r绑定的是什么?

>>>会打印出300和none。r绑定的是none(因为没有return,就返回none)

Def mysum(a,b):

Result = a + b

R = mysum(100,200)

Print(result)

#打印错误

>>>函数外部无法访问函数内部的局部变量

函数内部可以访问函数外部的变量,但不能修改函数外部变量的绑定关系

x = 0

Def myadd(a,b):

x = a + b

Print(x)

Myadd(100,200)

Print(x)

>>>0

>>>第一行的x和函数里面的x不是同一个x,只是同名而已。最后一行print的还是全局变量的x,0;函数里面的x是另外一个x。

二、函数的参数

2.1函数的参数传递

将实参传给函数

传递方式

函数示例为:

Def myfun(a,b,c):

   Print(a)

   Print(b)

   Print(c)

位置传参

myfun(A,B, C

实际传递参数(‘实参’)与形式参数(‘形参’)的对应关系按位置来依次对应

序列传参

L = [1.1, 2.2, 3.3]

myfun(*L

函数调用过程中用‘*’将序列拆解后按位置进行传递

ps:参数个数与序列元素要相等

关键字传参

myfun(b=2, c=3, a=1)

传参时,按形参的‘名称’给形参赋值,实参和形参按名称进行匹配

ps:传参时,只能给函数有的关键字,且不能重复

字典关键字传参

d = {‘a’:111,‘c’:333,‘b’:222}

Myfun(**d

输出结果为 111 222 333

实参为字典,将字典用“**”拆解后进行关键字传参的传参方式

说明:

1.字典的键名和形参名必须一致;

2.字典的键名必须为字符串(且必须为标识符的规则);

3.字典的键名要在形参中存在

综合传参

在能确定形参能唯一匹配到应当实参的情况下,可以任意组合;

位置传参在前,关键字传参在后。

myfun(1, c=3, b=2)   #正确

Myfun(b=2, a=1, 3)  #错误,位置参数要在前

Myfun(100, *[200, 300])  #正确

Myfun( *‘AB’,300 )     #正确

Myfun(*[100], c=300, b=200) 

 #正确,先传给a,是位置传参,后再传给b、c是关键字传参

Myfun(*“AB”,**{“c”:300})    #正确

2.2函数的形参定义

接收实参

2.2.1函数的缺省参数

语法

def 函数名(形参名1=默认实参1, 形参名2=默认实参2, …)

说明

1.缺省参数必须自右至左依次存在: 如果一个参数有缺省参数,则其右侧的所有参数都必须有缺省参数

def fa(a=1,b,c=3):       #是错的

def fb(a,b=10,c):        #是错的

2.缺省参数可以有0个或多个,甚至全部都是缺省参数

2.2.2 定义方式

位置形参

def 函数名(形参名1,形参名2, …):

    语句块

星号元组形参

def 函数名(*元组形参名):

语句块

作用:收集多余的位置传参

def fb(*args):
    '''args绑定一个元组'''
    print("实参的个数是:", len(args))
    print("args=", args)


fb()
fb(1, 2, 3, 4)

>>>args = (1, 2, 3, 4)

位置形参和星号元组形参都是接收位置传参。只不过星号元组形参还要接收多余的参数

命名关键字形参

Def 函数名(*,命名关键字形参)

Def 函数名(*args,命名关键字形参

作用:强制*后面所有的传参都必须使用关键字传参*相当于分隔符

# 此示例示意函数的命名关键字形参

def fa(a, b, *, c, d):
    '''强制c,d必须用关键字传参 '''
    print(a, b, c, d)

fa(1, 2, d=400, c=300)  # 对的


def fb(a, b, *args, c, d):
    print(a, b, args, c, d)

fb(1, 2, 3, 4, d=400, c=200)       # 1,2,(3,4),200,400
fb(1,2,3,4,5, **{'d':400, 'c':300})       #1,2,(3,4,5),300,400

# 问题:
# fb(1,2,3,4, c=400, d=300, e=500)  # 出错,e是多余的

# fa(1, 2, 3, 4)  # 错的

问题:

def fb(a, b, *args, c, d):

     print(a, b, c, d)

fb(1, 2, 3, 4, c=400, d=300, e=500)能否传参?

双星号字典形参

def 函数名(**字典形参名):

作用:收集多余的关键字传参。只能有一个

# 此示例示意双星号字典形参的用法
def fa(**kwargs):
    '''kwargs绑定字典'''
    print("多余的关键字传参的个数是:", len(kwargs))
    print("kwargs =", kwargs)

fa(a=10, b=20, c=30)	#{'a':10, 'c':30, 'b':20}

def fb(*args, a, **kwargs):
    print(args, a, kwargs)

fb(1,2,3, b=20, c=30, a=10)			#(1,2,3) 10 {'b':20,'c':30}

函数的参数说明

1.位置形参,星号元组形参,命名关键字形参,双星号字典形参,缺省参数可以混合使用

2.函数参数自左至右的顺序依次为:

位置形参、星号元组形参、命名关键字形参、双星号字典形参

Def fn(a,b, *args, c,d,**kwargs):

    pass

fn(1,2,3,4,c=100,d=200,e=300,f=400)

>>>1给a,2给b,3和4打包成一个元组(*args),c给c,d给d,e和f打包成一个字典(**kwargs)


三、函数的变量

函数名是变量,它在创建时绑定一个函数

一个函数可以作为另一个函数实参传递

Def f1():

    Print(‘f1被调用’)

Def f2():

    Print(‘f2被调用’)

Def fx(fn):

    Print(‘fn绑定的是:’,fn)

Fn()     #调用fn绑定的函数

Fx(f1)

Fx(f2)

目前有3个全局变量,f1,f2,fx

函数作为另一个函数的返回值

示例:

# 此示例示意get_op这个函数可以返回其它的函数,max/min/sum
def get_op():
    s = input("请输入您要做的操作: ")
    if s == '求最大':
        return max
    elif s == '求最小':
        return min
    elif s == '求和':
        return sum

L = [1, 2, 3, 4]
fx = get_op()		#fx绑定的是get_op返回的函数max/min/sum
print( fx(L) )		#然后调用max/min/sum其中的函数,求L的最大、最小、和


函数的嵌套定义

指一个函数里用def语句来创建其它函数的情况

示例:function_embed_def.py

# 此示例示意函数内部来嵌套定义其它函数
def fun_outer():
    print("fun_outer被调用...")
    # 在此处创建另一个函数,并在下面调用
    def fun_inner():
        print("fun_inner被调用")
    fun_inner()  # 调用一次
    fun_inner()  # 调用二次...

    print("fun_outer调用结束")
    return fun_inner

f = fun_outer() # 调用函数
f()  # 来调用 fun_outer里创建的那个fun_inner?

# fun_inner()  # 调用失败


四、函数的返回值

Return语句

语法

Return [ 表达式 ]

注:[ ] 代表内容可略

作用

结束当前函数的执行,返回到调用该函数的地方,同时返回表达式的引用关系

说明

1.Return语句后跟的表达式可以省略,省略后相当于return none

2.如果函数内没有return语句,则函数执行完最后一条语句后返回none(相当于在最后加了一条return none语句)

五、函数式编程

是指一系列函数解决问题

函数是一等公民(Guido says)

1.函数本身可以赋值给变量,赋值后变量绑定函数

2.允许将函数本身作为参数传入另一个函数

3.允许函数返回一个函数

好处

1.用每一个函数完成细小的功能,一系列函数在任意组合可以完成大问题

函数的可重入性

1.当一个函数在运行时不会读取和改变除局部作用域以外的变量时,此函数为可重入函数

2.可重入函数在每次调用时,如果参数一定,则结果必然一定

可重入函数示例

Def add1(x,y):

Return x+y

>>>add1(100,200)调用一万次,结果都一样

不可重入函数示例:

	
Y = 200

Def add2(x):

Return x + y

Print(myadd2(10))   #210

Y=300

Print(myadd2(10))   #310

六、递归函数 Recursion

函数直接或间接的调用自身

示例

#直接调用自己,进入递归

Def f():

    f()  

f()

#函数间接调用自身

Def fa():

    fb()

def fb():

    fa()

fa()

print(“递归完成”)

说明

1.递归一定要控制递归的层数,当符合一定条件时要终止递归调用

2.几乎所有递归都能用while循环来代替

优点

1.递归可以把问题简单化,让思路更为清晰,代码更简洁

缺点

1.递归因系统环境影响大,当递归深度太大时,可能会得到不可预知的结果

递归的两个阶段

递推阶段

从原问题出发,按递归公式递推从未知到已知,最终达到递归的终止条件

回归阶段

按递归终止条件求出结果,逆向逐步代入递归公式,回归到问题求解

递归的实现方法

先假设函数已经实现

练习

写一个函数mysum(n),用递归方法求

七、闭包closure

是指引用了此函数外部嵌套函数作用域变量的函数

闭包必须满足三个条件:

1.必须有内嵌函数

2.内嵌函数必须引用外部函数中的变量

3.外部函数返回值必须是内嵌函数

def make_power(y):

    def fn(x):       >>>条件1

        return x ** y     >>>条件2,局部变量y不会消失,还可以接着用

return fn      >>>条件3

 

pow2 = make_power(2)

print("5的平方是:",pow2(5))

pow3 = make_power(3)

print('6的立方是:',pow3(6))

    def fn(x):       >>>条件1

        return x ** y     >>>条件2,局部变量y不会消失,还可以接着用

return fn      >>>条件3

pow2 = make_power(2)

print("5的平方是:",pow2(5))

pow3 = make_power(3)

print('6的立方是:',pow3(6))

八、装饰器 Decorators

问题:

1.函数名是变量,它绑定一个函数

2.函数名  /函数名() 区别:

前者,绑定的是一个变量;后者,调用函数

装饰器定义

是一个函数,用来包装另一个函数或类

装饰器作用

是在不改变原函数名(或类名)的情况下改变被包装对象的行为

函数装饰器

指装饰器是一个函数,传入的是一个函数,返回的也是一个函数

语法

def 装饰器函数名(参数):

        语句块

        Return 函数对象

@装饰器函数名<换行>

def 函数名(形参列表):

        语句块

示例

Mydeco1.py

#mydeco1.py

# def mydeco(fn):
#     return None

# def myfunc():
#     print('函数myfunc被调用')

# myfunc = mydeco(myfunc)  #myfunc = None
# myfunc()   #相当于None()


#---------------------------------------------
def mydeco(fn):
    def fx():
        print("fx被调用")
    return fx

#myfunc加了mydeco装饰器,等同于在myfunc创建之后调用
#即,myfunc = mydeco(myfunc)
#将下面的myfunc传入mydeco,并赋值给myfunc(即,myfunc绑定mydeco的返回值)
@mydeco 
def myfunc():
    print('函数myfunc被调用')

#myfunc绑定的是mydeco返回的fx, myfunc=fx;
#这样的写法可以用装饰器来代替
# myfunc = mydeco(myfunc)  

myfunc()     
#加了@mydeco   >>> fx被调用 
#不加@mydeco   >>> myfunc被调用

Mydeco2.py

def mydeco(fn):
    def fx():
        print("+++++++++++这是myfunc被调用之前++++++++++")
        #要想在此处调用被装饰的函数myfunc,怎么办?
        fn()   
        #用fn调用被装饰函数,因为在传参的时候fn = myfunc
        #不用myfunc,因为已被赋与了mydeco的返回值
        #调用fn,形成一个闭包,不会释放myfunc
        print("+++++++++++这是myfunc被调用之后++++++++++")
    return fx



@mydeco 
def myfunc():
    print('函数myfunc被调用')


myfunc() 

Test_deco.py

def mydeco(fn):
    print("装饰器函数被调用了...")
    def fx():
        print('fx被调用了')
    return fx



# @mydeco #加 不加装饰器结果一样吗??
def myfunc():
    print('func被调用了')


myfunc()
myfunc()  #调用第二次
myfunc()  #调用第三次


#加装饰器 打印结果
# 装饰器函数被调用了...
# fx被调用了  # myfunc = mydeco(myfunc)
# fx被调用了  # myfunc()相当于fx(),所以没有'装饰器函数被调用了...'
# fx被调用了

#不加装饰器 打印结果
# fx被调用了
# fx被调用了
# fx被调用了

Mydeco4.py

#示范 装饰器在不改变原函数和调用者行为的情况下
#     来改变原有函数功能


#装饰器函数
def privillage_check(fn):
    def fx(name, x):
        print('正在检查权限...')
        fn(name, x)   #权限通过,则可以调用相应函数
    return fx




#写一个操作数据的函数(存钱操作)
@privillage_check
def savemoney(name, x): 
    print(name,'存钱',x,'元')

@privillage_check
def withdraw(name, x):
    print(name,'取钱',x,'元')

#---------------------以下是调用者
savemoney('小张',200)
savemoney('小赵',400)

withdraw('小李',500)

Mydeco5.py

#示范 再加一个装饰器来添加余额变动提醒功能

def send_message(fn):
    def fy(name, x):
        fn(name, x)   #先办理业务,再发送余额提醒
        print('发短信给',name, '办理了',x,'元')

    return fy

#装饰器函数
def privillage_check(fn):
    def fx(name, x):
        print('正在检查权限...')
        fn(name, x)   #权限通过,则可以调用相应函数
    return fx




#send_message修饰的是fx
#因为privillage_check返回的是fx
@send_message   
@privillage_check
def savemoney(name, x): 
    print(name,'存钱',x,'元')

@send_message
@privillage_check
def withdraw(name, x):
    print(name,'取钱',x,'元')

#---------------------以下是调用者
savemoney('小张',200)
savemoney('小赵',400)

withdraw('小李',500)

九、函数的文档字符串

9.1 文档字符串

函数内第一次未赋值给任何变量的字符串是此函数的文档字符串

语法

def 函数名(形参列表):

        ’’’函数的文档字符串’’’

        函数语句块

示例

def hello():

         ’’’此函数用来打招呼。。。’’’

          Pass

>>>help(hello)

说明

1.文档字符串通常用来说明本函数的功能和使用方法

2.在交互模式下,输入:help(函数名)

可以查看函数的文档字符串

不是注释,它要占用运行空间(有变量来绑定它),并且也要编译执行

9.2__doc__属性

用于记录函数的文档字符串

交互模式下,输入 函数名 . __doc__

9.3 __name__属性

用于记录函数的名称

Def hello():

  Pass

f = hello

f.__name__      #交互模式下,求f绑定的函数的名字

# >>>‘hello’

Del hello       #删除变量

f.__name__

# >>>‘hello’  #del只是删除hello变量,实际的hello()还在

十、函数总结

定义语法

@装饰器1

@装饰器2

。。。

Def 函数名(位置形参, *元组形参(或*), 命名关键字形参, **字典形参):

      ’’’文档字符串’’’

      语句块

思考题:test.py

L = [1, 2, 3]
def f(n=0, lst=[]):
    lst.append(n)
    print(lst)

f(4, L)     #[1, 2, 3, 4]
f(5, L)     #[1, 2, 3, 4, 5]
f(100)      #[100]
# print('123:',lst)# 打印不出来,外面访问不到函数里面的lst
f(200)      #[100,200],f的缺省参数lst会一直存在


#---------------------------------------------------
L = [1, 2, 3]
def f(n=0, lst=None):   #*****modified******
    if lst is None:     #官方给的solution
        lst = []
    lst.append(n)
    print(lst)

f(4, L)     #[1, 2, 3, 4]
f(5, L) 
f(100)      #[100]
f(200)      #[200]






附注:

一、局部变量和全局变量

1.1局部变量

1.是定义在函数内部的变量(函数的形参也是局部变量)

2.局部变量只能在函数内部使用

3.局部变量在函数调用时,才能够被创建;在函数调用结束时,会自动销毁;如果还有其它变量绑定,则不会销毁。示例:function_embed_def2.py

# 此示例示意函数内部来嵌套定义其它函数
def fun_outer():
    print("fun_outer被调用...")
    # 在此处创建另一个函数,并在下面调用
    def fun_inner():
        print("fun_inner被调用")
    fun_inner()  # 调用一次
    fun_inner()  # 调用二次...

    print("fun_outer调用结束")
    return fun_inner

f = fun_outer() # 调用函数
f()  # 来调用 fun_outer里创建的那个fun_inner?

# fun_inner()  # 调用失败

(闭包closure除外)

说明:

1.在函数内首次对变量赋值是创建局部变量,再次为变量赋值是修改局部变量的绑定关系

2.在函数内部的赋值语句不会对全局变量造成影响

3.局部变量只能在其被声明的函数内部访问,而全局变量可以在整个模块被访问

1.2全局变量

1.是定义在函数外部,模块内部的变量

2.所有函数都可以直接访问全局变量,但函数内部不能直接通过赋值语句来改变全局变量

二、python的作用域

2.1 四个作用域

也叫名字/命名空间(name space),是访问变量时查找变量名的范围空间

四个作用域:LEGB

作用域

英文解释

英文简写

局部作用域(函数内)

Local(function)

L

外部嵌套函数作用域

Enclosing function locals

E

函数定义所在模块(文件)的作用域

Globals(module)

G

Python内置模块的作用域

Builtin(python)

B

 

2.2 变量名的查找规则

1.在访问变量时先查找本地变量,然后是包裹此函数外部的函数内部的变量,之后是全局变量,最后是内置变量(L---->E ---->G---->B)

2.在默认情况下,变量名赋值会创建或者改变本地作用域变量

2.3 Global语句

作用

1.告诉解释器,global语句声明的一个或多个变量,这些变量的作用域为模块级的作用域(也称作全局变量)

2.全局声明(global)将赋值的变量映射到模块文件内部的作用域

语法

Global 变量1,变量2, …

示例

Global v, a, b, c

Global d

global说明

1.全局变量如果要在函数内部被赋值,则必须经过全局声明(否则会被认为是局部变量)

2.全局变量在函数内部不经过声明就可以直接访问

“访问”:获取绑定关系,而不是改变变量

3.不能先创建局部变量,再用global声明为全局变量,此做法不符合规则

4.global变量列表里的变量名不能出现在此作用域内形参列表里

(不能既是参数、又是全局变量)

2.4 nonlocal语句

作用

告诉解释器,nonlocal声明的变量不是局部变量,也不是全局变量,而是外部嵌套函数内的变量

语法

Nonlocal 变量名1,变量名2,…

说明

1.nonlocal语句只能在被嵌套的函数内部进行使用

2.访问nonlocal变量将对外部嵌套函数作用域内的变量进行操作

3.当有两层或两层以上函数嵌套时,访问nonlocal变量只对最近一层的变量进行操作

4.nonlocal语句的变量列表里的变量名,不能出现在此函数参数列表中


猜你喜欢

转载自blog.csdn.net/weixin_37767152/article/details/80718326
011
今日推荐