Python(函数)

本文详细介绍了Python函数的概念、定义、参数、调用、返回值、函数的描述、高级使用技巧,如偏函数、高阶函数、返回函数、匿名函数、闭包、装饰器,以及生成器和递归函数。同时讲解了函数作用域的LEGB规则和变量类型。
摘要由CSDN通过智能技术生成

Python基础
-5-函数

一、概念

写了一段代码实现了某个小功能;然后把这些代码集中到一起,起了一个名字;下一次就可以根据这个名字再次使用这个代码,这就是函数。
作用:(1)方便代码重用,(2)分解任务,简化程序逻辑,

二、定义

def 函数名():
    函数体

函数的定义相当于开辟一块内存空间,定义一个与函数名相同的变量,指向这个函数 

三、函数的参数、调用、返回值

形参:函数定义中的“参数名称”
实参:调用函数时传递的“真实数据(参数值)”

单个参数:
def 函数名(参数名称):
    函数体
多个参数
def 函数名(参数名称1,参数名称2,……):
    函数体
不定长参数

(1)以元组的方式接收不定长参数,传入的参数以元组的形式保存在args中
def  函数名(*args):
    函数体

(2)以字典的方式接收不定长参数,传入的参数以字典的形式保存在kwargs中

def  函数名(**kwargs):
    函数体

(3)拆包与装包

装包->把传递的参数包装成一个元组或字典,这个过程就称之为装包
xx不定长参数函数(参数1,参数2....)
拆包->把元组、字典里面的参数,再次分解成单个的个体,称为拆包
xx函数(*args)

#累加功能
def add(*args):
    num = 0
    for i in args:
        num += i
    return num
#累乘功能
def multiply(*args):
    num = 1
    for i in args:
        num *= i
    return num
#计算功能函数,一个参数接受一个运算符+或*
#实现累加或累乘功能
def appliction(app,*args):
    if app == "+":
        #拆包
        print(add(*args))
    elif app == "*":
        #拆包
        print(multiply(*args))
    else:
        print("请正确输入".center(20,"-"))
#装包
appliction("+", 1, 5, 8, 9, 10)
appliction("*", 1, 5, 8, 9, 10)
appliction("-", 1, 5, 8, 9, 10)
#运行结果
33
3600
-------请正确输入--------

缺省参数:包含其他参数时,缺省参数放最后

使用函数时,当不填写参数时,使用默认值

def 函数名(变量名1 = 默认值1 , 变量名2 = 默认值2):
    函数体

 调用

不带参数函数
函数名()

带参数函数
函数名(参数1,参数2......)  -这种方式实参和形参必须一一对应
函数名(参数名=参数1,参数名=参数2......)-这种方式实参和形参不必一一对应

Python参数传递:引用传递(地址传递)
但是修改一个不可变类型时,其实是让其指向一个新的变量

def test1(num):
    print(id(num))
a = 10
print(id(a))
test1(a)
print("测试分割线".center(20, "-"))
def test2(num):
    num = 20
    print(id(num))
a = 10
print(id(a))
test2(a)
print("测试分割线".center(20, "-"))
def test3(num):
    num.append(20)
    print(id(num))
a = [5, 8]
print(id(a))
test3(a)
#运行结果
1588555520
1588555520
-------测试分割线--------
1588555520
1588555840
-------测试分割线--------
1460288630664
1460288630664

返回值

作用:当我们通过某个函数,处理好数据以后,想要拿到处理结果

注:(1)return后续代码不会被执行

        (2)只能返回一次

       (3)如果想要返回多个数据,可以把多个数据包装成一个“集合“,整体返回

四、函数的描述

作用:当我们编写三方函数,为了方便他人使用,就需要描述清楚我们所写的函数功能以及使用方式等信息 

定义格式:


def   函数名():
    """这里写帮助信息"""
    函数体

一般函数的描述,需要说明以下几个信息:
(1)函数的功能
(2)参数: 含义、类型、是否可省略、默认值
(3)返回值:含义、类型

查看函数使用文档:help(函数名)

五、函数的高级使用

(1)偏函数:当我们写一个参数比较多的函数时,如果有些参数,大部分情况下都是某一个固定值;那么为了简化使用,就可以创建一个新函数,指定我们要使用的函数的某个参数为某个固定值;这个新函数就是“偏函数”

(2)高阶函数:当一个函数A的参数,接收的又是另一个函数时,这是把这个函数A称为高阶函数

(3)返回函数:是指一个函数内部,它返回的数据是另外一个函数;这样的操作称为“返回函数”

(4)匿名函数:也称为“lambda函数”

语法:


lambda 参数1 参数2 ... :表达式


表达式的结果就是返回值,只适用于一些简单的操作处理

(5)闭包

在函数嵌套的前提下,内层函数引用了外层函数的变量(包括参数),外层函数,又把‘内层函数’当做返回值进行返回。
这个内层函数+所引用的外层变量,称为“闭包”
标准格式:

def test(a):
    b = 10
    #定义了一个内部函数
    def inner():
    #内部函数引用了外层变量a、b
        c = 20
        print(a*b*c)
    #把内部函数返回
    return inner

注意事项:

(1)闭包中,如果要修改引用的外层变量
        需要使用  nonlocal 变量声明,否则当做是闭包内新定义的变量

(2)当闭包内,引用了一个后期会发生变化的变量时,一定要注意,当函数被调用时,才会真正确定对应值;之前都是以普通的变量标识存在。

(6)装饰器

作用:在函数名以及函数体不改变的前提下,给一个函数附加一些额外代码

原理:使用闭包

#定义一个xx装饰器,接收一个函数
def zsq(func):
    def inner():
        """添加新的功能"""
        print("添加的新功能")
        func()
    return inner
def test1():
    """一些功能"""
    print("test1的功能")
#把test变量指向装饰器返回的函数inner
#inner里面又调用了test函数
#这样就看起来给test函数增加了新功能
test1 = zsq(test1)
#语法糖写法
#@zsq 等价于 test2 = zsq(test2)
@zsq
def test2():
    """一些功能"""
    print("test2的功能")
test1()
print("测试分割线".center(20,"-"))
test2()
#运行结果
添加的新功能
test1的功能
-------测试分割线--------
添加的新功能
test2的功能

注:装饰器执行时间,立即执行 

装饰器进阶使用:

(1)装饰器叠加

从上到下“装饰“(添加新增代码)
从下到上执行(执行装饰器)

def zsq_1(func):
    def inner():
        print("-"*20)
        func()
    print("zsq1已经装饰完毕")
    return inner
def zsq_2(func):
    def inner():
        print("*"*20)
        func()
    print("zsq2已经装饰完毕")
    return inner
@zsq_1
@zsq_2
def test():
    print("吾所成之事 ,不可逆也")
print("测试分割线".center(20, "-"))
test()
#运行结果
zsq2已经装饰完毕
zsq1已经装饰完毕
-------测试分割线--------
--------------------
********************
吾所成之事 ,不可逆也

(2)对有参函数进行装饰

无论装饰什么函数,保证函数调用参数个数一致
为了通用,可以使用不定长,结合拆包操作进行处理

def zsq(func):
    def inner(*args):
        print("-"*20)
        func(*args)
    return inner
@zsq
def test1(a, b):
    print("两个数之和为:%d" % (a + b))
test1(10, 20)
@zsq
def test2(a, b, c):
    print("三个数之和为:%d" % (a + b +c))
test2(10, 20, 30)
#运行结果
--------------------
两个数之和为:30
--------------------
三个数之和为:60

(3)对有返回值的函数进行装饰

直接返回函数调用

def zsq(func):
    def inner(*args):
        print("-"*20)
        return func(*args)
    return inner
@zsq
def test1(a, b):
    return "两个数之和为:%d" % (a + b)
t1 = test1(10, 20)
@zsq
def test2(a, b, c):
    return "三个数之和为:%d" % (a + b +c)
t2 = test2(10, 20, 30)
print(t1, t2, sep="\n")
#运行结果
--------------------
--------------------
两个数之和为:30
三个数之和为:60

(4)带有参数的装饰器

为了保证能使用语法糖写法:通过一个函数传递参数后,返回一个装饰器

def get_zsq(char):
    def zsq(func):
        def inner(*args):
            print("分割线".center(40, char))
            func(*args)
        return inner
    return zsq

@get_zsq("-")
def test1(a, b):
    print("两个数之和为:%d" % (a + b))

@get_zsq("&")
def test2(a, b, c):
    print("三个数之和为:%d" % (a + b + c))

test1(10, 20)
test2(20, 30, 52)
#运行结果
------------------分割线-------------------
两个数之和为:30
&&&&&&&&&&&&&&&&&&分割线&&&&&&&&&&&&&&&&&&&
三个数之和为:102

(7)生成器(不是函数)
当函数里面包含yeild语句,函数就变成了生成器

概念:是一个特殊的迭代器(迭代器的抽象层次更高),所以,拥有迭代器的特性惰性计算数据,节省空间,数据要使用时,才取,并且一个一个取能够记录状态,并通过next()函数,访问下一个状态,具备可迭代性

创建方式:

方式一:生成器推导式  - - 把列表推导式的[ ]修改成( )

方式二:函数中包含yield语句,这个函数的执行结果就是“生成器"
yeild语句作用:可以阻断当前的函数执行;当使用next()函数或者_ _next()_ _方法都会让函数继续执行,然后当执行到下一个yield语句时,又会被暂停,并返回yield语句后面的表示的值;

gen = (a for a in range(10) if a%2==0)
print(type(gen))
print(next(gen))
print(gen.__next__())
for a in gen:
    print(a)
print("测试分割线".center(20, "-"))

#斐波拉契数列生成器
def gen():
    count = 1
    a, b = 0, 1
    while True:
        print("--第%d次--"%count)
        count += 1
        yield a
        a, b = b, a + b
g = gen()
type(g)
print(next(g))
for i in range(10):
    print(next(g))
#运行结果
<class 'generator'>
0
2
4
6
8
-------测试分割线--------
--第1次--
0
--第2次--
1
--第3次--
1
--第4次--
2
--第5次--
3
--第6次--
5
--第7次--
8
--第8次--
13
--第9次--
21
--第10次--
34
--第11次--
55

获取数据方式:
使用next()函数
使用生成器对象__next__()方法

for  in 循环

生成器对象send()方法:send方法可接收一个参数,指定的是上一次被挂起的yield语句的返回值(注意第一次调用:g.send(None))

def gen():
    recv1 = yield "第一次"
    print(recv1)
    recv2 = yield "第二次"
    print(recv2)
    recv3 = yield "第三次"
    print(recv3)
    recv4 = yield "第四次"
    print(recv4)
g = gen()
print(g.send(None))
print(g.send("枫叶"))
print(g.send("银杏"))
第一次
枫叶
第二次
银杏
第三次

关闭生成器:
生成器对象close()方法,后续如果继续使用会抛出StopIteration异常
注意:(1)如果碰到return,会直接终止,抛出StopIteration异常(2)生成器只会遍历一次

(8)递归函数

函数A的内部,继续调用函数A

 注:有传递就有回归,不然会陷入死循环

#求一个数字的阶乘
def jc(n):
    if n == 1:
        return 1
    return n*jc(n-1)
print(jc(8))
print("测试分割线".center(20, "-"))
#求斐波拉契数列第n项的值,n大于等于2
def fblq(n):
    if n == 1:
        return 1
    if n == 0:
        return 0
    return fblq(n-1) + fblq(n-2)
print(fblq(10))
#运行结果
40320
-------测试分割线--------
55
import time
#倒计时函数
def r_time(n):
    print("倒计时:%d s" % n)
    time.sleep(1)
    n -= 1
    if n == 0:
        print("游戏结束")
        return "结束"
    r_time(n)
r_time(5)
#运行结果
倒计时:5 s
倒计时:4 s
倒计时:3 s
倒计时:2 s
倒计时:1 s
游戏结束

六、函数作用域

变量作用域:变量的作用范围,即可操作范围,Python是静态作用域,也就是说在Python中,变量的作用域源于它在代码中的位置;在不同的位置,可能有不同的命名空间


命名空间:是作用域的体现形式,不同的具体操作范围
Python-LEGB:

L - local: 函数内的命名空间,作用范围:当前整个函数体范围
E - Enclosing  function locals:外部嵌套函数命名空间, 作用范围:闭包函数
G - Global:全局命名空间,作用范围:当前模块文件
B - Builtin:内建模块命名空间,作用范围:所有模块(文件)
注意: Python没有块级作用域,块级作用域,代码块中,比如  if ,while ,for后的代码块中
LEGB访问规则:按照 L -> E -> G -> B的顺序进行查找


基于命名空间的常见变量类型:
局部变量:在一个函数内部定义的变量,作用域为函数内部

查看局部变量: locals()

全局变量:在函数外部,文件最外层定义的变量,作用域为整个文件内部

查看全局变量:globals()

注:

(1)全局变量和局部变量重名,获取,就近原则 。

(2)函数内修改全局变量-声明      global 全局变量

num = 10
def test():
#函数内声明num使用的是全局变量num
    global num
    num = 100
test()
print(num)
#运行结果
100

(3)命名:尽可能使用  g_xxx  形式命名全局变量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值