Python之旅6 - Python函数合辑

 1、函数是什么?

函数指的是一段可以被重复使用的代码,是一种实现代码重用与代码维护的技术手段。
简单来说,就是将我们写好的一段程序代码封装到函数中,在下次我们需要使用时,直接调用函数名就可以直接使用了,不需要再继续进行编写重复的代码,也就是使用函数实现模块化的程序设计

 

2、函数的定义:

def function_name([parameter,.....])
        ['''comments''']
        function body
        [return [返回值]]

说明:
使用def关键字来定义一个函数,
function_name 函数名称,在调用函数时使用
parameter 可选参数,即用于指定想函数中传递的参数,如果有多个,各参数之间使用逗号隔开,如果不指定,则表示这个函数没有参数,在调用的时候也不需要传递参数
comments 为函数的注释说明,通常是说明该函数的功能,要传递的参数的作用,如果有返回值是,提示返回值说明
function body 为函数体,即当函数被调用后,需要执行的功能代码。
return 可选参数,如果有返回值是 使用return语句来返回函数执行之后的返回值。

示例:函数的创建和调用

复制代码
 1 def fun_bmi(heigth, weight):  # 创建函数
 2     """
 3     功能:根据身高体重计算BMI指数
 4     :param heigth: 身高(单位m)
 5     :param weight: 体重(单位kg)
 6     :return: 返回BMI指数计算结果
 7     """
 8     bmi = weight / (heigth * heigth)
 9     if bmi < 18.4:
10         return '偏瘦'
11     elif 18.4 <= bmi < 23.9:
12         return '正常范围'
13     elif 23.9 <= bmi < 27.9:
14         return '偏胖'
15     else:
16         return '肥胖'
17 
18 
19 result = fun_bmi(1.72, 75)  # 调用函数,result用于接收函数的返回值
20 print(result)
复制代码

 

pass关键字:pass关键字,即占位符。表示空语句,不做任何事情,在实际开发过程中如果我们还没有想好函数要实现什么功能可以使用pass语句填充函数体 function body

函数的形参和实参理解:
形参:在定义函数时,函数名称后面括号内的参数,即为“形式参数”也称“形参”
实参:在调用函数时,函数名称后面括号内的参数,即为“实际参数”也称“实参”

位置参数:
位置参数也被称为必须参数,必须按照正确的顺序传递到函数中。即我们在调用函数时传递参数的数量和位置必须和定义函数时是一样的。

关键字参数:
是指在调用函数时,使用形参的名字来确定输入的参数值,这样就不再需要与形参的位置完全一样了,可以避免用户需要牢记参数位置的麻烦,也使得函数的调用和参数的传递更加方便灵活。

为参数设置默认值:
调用函数时,为了避免没有为某个参数指定实参将抛出异样的情况,直接指定形参的默认值,即直接在定义函数时,为形参赋值。
注意事项:默认值参数必须放在所有参数之后,否则会产生语法错误

可变参数
在python中,还可以定义可变参数,可变参数也称为不定长参数,即传入函数中的实参可以是0个,可以是1个,2和到任意个

定义可变参数,有2种形式
*args 表示接收任意多个实参并将其放到一个元组中
示例:

复制代码
1 def func_args(*args):
2     for i in args:
3         print(i)
4 
5 
6 func_args('hello world')
7 func_args('h', 'e')
8 func_args('hello', 'python', '人生苦短,我学python')
复制代码

  执行结果: 

hello world、
h、e、
hello、python、人生苦短,我学python、

进程已结束,退出代码0

 

**kwargs 表示接收任意多个显示赋值的实参,并将其放到一个字典中

示例:

1 def func_kwargs(**kwargs):
2     for k, v in kwargs.items():
3         print("%s的年龄是%d岁" % (k, v))
4 
5 
6 func_kwargs(zhangsan=18)
7 func_kwargs(zhangsan=18, lisi=20, wangwu=22)

  执行结果: 

zhangsan的年龄是18岁、
zhangsan的年龄是18岁、lisi的年龄是20岁、wangwu的年龄是22岁、

进程已结束,退出代码0

  

多种类型参数的定义顺序:必须参数,默认参数,可变参数,命名关键字参数和关键字参数

 

3、函数的递归调用 

递归是函数调用的一种特殊形式,指的是函数自己调用自己的形式。利用递归调用可以解决一些重复且有序的操作。
递归调用的关系图: 

 

 在进行函数递归操作时必须满足的2个条件:
① 递归调用必须要有结束条件(否则会出现死循环)
② 每次调用的时候都需要根据需求改变传递的参数内容

 示例:实现1~100的数字累加 

复制代码
 1 def get_sum(num):
 2     """
 3     数值累加求和
 4     :param num: 要进行累加数值的最大值
 5     :return: 返回累加结果
 6     """
 7     if num == 1:
 8         return 1
 9     return num + get_sum(num - 1)
10 
11 
12 result = get_sum(100)
13 print(result)
复制代码

  

4、变量作用域
变量作用域是指一个变量可以使用的范围,例如在函数中,我们定义的变量称为局部变量,局部变量只能在当前代码块中被访问;而在非函数体中定义的变量称为全局变量,全局变量是允许可以在多个函数体或代码中共同访问。

关于变量名解析的LEGB原则(它规定了在一个python程序中变量名的查找顺序):
L (Local)函数体内部变量名称
E (Enclosing Function Locals)外部嵌套函数变量名称
G (Global)函数所在模块或程序文件的变量名称
B (Builtin)内置模块的变量名称

搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。

 

 示例:局部变量和全局变量的区别:

复制代码
 1 year = 2022
 2 
 3 
 4 def change_year_1():
 5     year = 2000
 6     print("函数体中的局部变量year的值为%d" % year)
 7 
 8 
 9 change_year_1()
10 print("函数体外的全局变量year的值为%d" % year)
11 
12 print("加上global关键字之后,代码执行效果变化~~~~~~~~~~~~~~~~~~~~~~~~~~~")
13 
14 
15 def change_year_2():
16     global year
17     year = 1990
18     print("函数体中的局部变量year的值为%d" % year)
19 
20 
21 change_year_2()
22 print("函数体外的全局变量year的值为%d" % year)
复制代码

 执行结果:

函数体中的局部变量year的值为2000
函数体外的全局变量year的值为2022
加上global关键字之后,代码执行效果变化~~~~~~~~~~~~~~~~~~~~~~~~~~~
函数体中的局部变量year的值为1990
函数体外的全局变量year的值为1990

进程已结束,退出代码0

 

同个示例对比可以发现,函数中定义的year变量在没有使用global关键字时,为局部变量;

在使用global关键字之后,year为全局变量 

在python中,提供了2个函数可以动态的获取程序文件中的全局变量和局部变量

globals() 获取全局变量,并以字典形式返回

localls() 获取局部变量,并以字典形式返回

 

5、闭包

引入闭包概念前我们先思考下这么一个问题:

局部变量可以访问全局变量,当实际开发中,我们想从函数的外部访问函数的局部变量内容时,由于python的变量作用域通常我们就不能实现这个需求。

但当我们在函数内部再定义一个内部函数,并返回内部函数,就可以得到局部变量内容了,这样的一个函数结构就称为函数的闭包。

示例:

复制代码
 1 def get_value():
 2     var = "这是我们想从外部访问的变量var"
 3 
 4     def inner():  # 定义一个内部函数
 5         return var  # 从内部函数访问var 并返回
 6 
 7     return inner  # 返回inner函数对象
 8 
 9 
10 result = get_value()  # get_value()的返回内容是inner函数对象,这里的result就是函数对象
11 print(result())  # 调用函数对象,在result后加()
复制代码

 执行结果:

这是我们想从外部访问的变量var

进程已结束,退出代码0

  闭包的定义:

闭包就是外部函数中定义一个内部函数,内部函数引用外部函数中的变量,外部函数的返回值是内部函数对象的引用(3者必须同时满足才能称为闭包)

闭包和嵌套函数类似,不同之处在于,闭包中外部函数返回的不是一个具体的值,而是一个函数对象的引用。

一般情况下,返回的函数会赋值给一个变量,这个变量可以在后面被继续执行调用。

 

 闭包中的内函数修改外函数的局部变量 nonlocal关键字的使用

 示例: 

复制代码
 1 def outer(count):
 2     def inner(msg):
 3         nonlocal count  # 使用nonlocal关键字,如果此时不加nonloc关键字则表示本地变量(内函数局部变量)
 4         count += 1  # 修改外部变量count的值
 5         return print("第{}次传递的信息为:{}".format(count, msg))
 6 
 7     return inner
 8 
 9 
10 send = outer(0)  # 初始化计数数值  即设置count=0
11 send("hello")
12 send("人生苦短,我学python")
复制代码

  执行结果:

第1次传递的信息为:hello
第2次传递的信息为:人生苦短,我学python

进程已结束,退出代码0

 

6、装饰器 decorator

 

了解闭包的原理之后,其实可以理解为装饰器函数也是闭包的一种,只不过闭包传递的是变量,而装饰器传递的是函数。也可以理解为这是装饰器和闭包的区别

本质上,装饰器是一个函数,用来处理其他函数,

装饰器的作用:

可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器返回的对象也是一个函数.

 

常用的装饰器需求场景有:插入日志、性能检测、事务处理、缓存、权限校验等

 

示例:简单的装饰器

 

复制代码
 1 def show():
 2     print("这是最初定义好的函数")
 3 
 4 
 5 def decorator(fun):
 6     def warpper():
 7         print("在%s()函数之前增加的功能" % fun.__name__)
 8         fun()
 9         print("在%s()函数之后增加的功能" % fun.__name__)
10 
11     return warpper
12 
13 
14 show = decorator(show)
15 
16 
17 def main():
18     show()
19 
20 
21 if __name__ == "__main__":
22     main()
复制代码

 

 

 

执行结果:

 

在show()函数之前增加的功能
这是最初定义好的函数
在show()函数之后增加的功能

进程已结束,退出代码0

 

 

 

示例代码中的main()主函数中调用的show()函数,但通过装饰器在不修改show()函数的前提下,

定义一个装饰器decorator传入一个函数fun 再返回wrapper, wrapper对fun的功能进行了增加,通过代码show = decorator(show)进行了装饰

即:使用装饰器decorator对show()函数进行包装,来修改它的功能,通常情况下不使用show = decorator(show)这种方式来进行装饰器的操作,而是使用@来语法糖来进行装饰:

复制代码
 1 def decorator(fun):
 2     def warpper():
 3         print("在%s()函数之前增加的功能" % fun.__name__)
 4         fun()
 5         print("在%s()函数之后增加的功能" % fun.__name__)
 6 
 7     return warpper
 8 
 9 
10 @decorator  # 等价于show = decorator(show)
11 def show():
12     print("这是最初定义好的函数")
13 
14 
15 def main():
16     show()
17 
18 
19 if __name__ == "__main__":
20     main()
复制代码

 

装饰器分为:不带参数的装饰器 和  带参数的装饰器

示例:带参数的装饰器

 

复制代码
 1 def decorator(fun):
 2     def warpper(*args, **kwargs):
 3         print("在%s()函数之前增加的功能" % fun.__name__)
 4         fun(*args, **kwargs)
 5         print("在%s()函数之后增加的功能" % fun.__name__)
 6 
 7     return warpper
 8 
 9 
10 @decorator  # 等价于show = decorator(show)
11 def show():
12     print("这是最初定义好的函数")
13 
14 
15 def main():
16     show()
17 
18 
19 if __name__ == "__main__":
20     main()
复制代码

 

 

 

 带参数的装饰器,即当要装饰的函数有参数时,在warpper函数传入*args和**kwagrs,使用*args和**kwagrs获取任意数量的参数,再传给要装饰的函数decorator(这里的decorator是我们示例中定义的)

如果函数有返回值,则在warpper函数内使用return返回即可

示例:一个简单的代码性能检测和插入日志

复制代码
 1 import time
 2 
 3 
 4 def insert_log(flag=False):
 5     def show_time(function_name):
 6         def wrapper():
 7             start_date = time.time()
 8             time.sleep(1)  # 设置程序停留时长
 9             function_name()
10             end_date = time.time()
11             if flag:
12                 print("日志插入成功!")
13             print("程序执行时间为", end_date - start_date)
14 
15         return wrapper
16 
17     return show_time
18 
19 
20 @insert_log(True)  # 相当于insert_log=show_time(my_function)
21 def my_function():
22     print("我的测试代码")
23 
24 
25 def main():
26     my_function()
27 
28 
29 if __name__ == "__main__":
30     main()
复制代码

 

 

7、匿名函数

匿名函数也成为lambda函数,一般适用于简短函数体的函数,在python实际开发中,我们有可能定义的函数只会使用一次,就可以定义一个匿名函数。

语法格式:var_name = lambda parameter1,parame2,... : function_body

示例:

get_sum = lambda a, b: a ** b

print(get_sum(3, 5))

 

8、 主函数

简单来说就是一个程序的起点

python可以在一个源代码文件中定义所需的代码,并且可以根据顺序执行,而python的灵活性并没有提供主函数的定义结构,但可以通过__name__的系统变量来进实现。

示例:定义主函数

 

复制代码
 1 def show():  # 随便定义的一个函数
 2     pass
 3 
 4 
 5 def main():  # 定义主函数
 6     show()
 7 
 8 
 9 if __name__ == "__main__":  # 通过__name__系统变量来操作
10     main()
复制代码

 

 

 

 

9、内置对象函数(常用的内置函数)

dict()  用于创建一个字典

help() 用于查看函数或者模块用途的详细说明(说明文档)

dir()  用于查看参数、变量、定义的方法和类型列表

  不带参数时,返回当前范围内的变量、方法、和定义的类型列表

  带参数时,返回参数的属性、方法列表

  如果参数包含__dir__(),该方法将被调用

  如果参数不包含__dir__(),该方法将最大限度的收集参数信息

bin()  返回整形(int)或者长整形(long int)的二进制表示;

oct()  将一个整数转换成八进制字符串

hex()  将一个整数转换成十六进制字符串

next()   返回迭代器下一个项目

divmod()   把除数和余数的运算结果结合起来,返回一个包含商和余数的元组(a//b,a%b)

sorted()   对所有可迭代对象进行排序操作,默认为升序排序

ascii()   返回一个表示对象的字符串,对于字符串中非ascii字符则返回通过repr()函数使用\x,\u或\U编码的字符

open() 打开文件

str()   将对象转化为适于人阅读的形式

sum()   对序列进行求和计算

filter()   用于过滤序列,过滤掉不符合条件的元素,返回有符合条件组成的新列表

format()   格式化字符串

callable()   判断给定的对象是否可以调用,如果可以调用则返回True,否则返回False

eval() 对给定的对象(可能是字符串或编译对象)进行单行代码计算,可以选择性地传递全局变量或局部变量信息(类型必须为字典)

exec()    对给定的对象进行多行代码计算,可以选择性的传递全局变量或局部变量信息

complie()   对传入的source数据进行编译,可以通过fliename指定需要编译的代码文件,在编译时需指明运行模式

mode='eval' 使用eval()函数执行代码

mode='single' 使用single()函数执行单行代码

mode='exec'  使用exec()函数执行多行代码

 

 

posted @   陈亦洺  阅读(46)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示