【Python必备基础核心篇】:函数案列与进阶

简介: 【Python必备基础核心篇】:函数案列与进阶

前言
大家好~ 我是辣条哥~

今天我们通过一些案例来为大家讲解函数的应用。基础里面最核心的内容,但凡是Python这个就是一定要看的,看完之后再开始敲击你的机械键盘,这样你才是最靓的仔~*

目录
前言
经典小案例
案例1:设计一个生成验证码的函数。
案例2:设计一个函数返回给定文件的后缀名。
案例3:写一个判断给定的正整数是不是质数的函数。
案例4:写出计算两个正整数最大公约数和最小公倍数的函数。
案例5:写出计算一组样本数据描述性统计信息的函数。
案例小总结
函数使用进阶
关键字参数
高阶函数的用法
Lambda函数
总结

经典小案例
案例1:设计一个生成验证码的函数。
说明:验证码由数字和英文大小写字母构成,长度可以用参数指定。

import random
import string

ALL_CHARS = string.digits + string.ascii_letters

def generate_code(code_len=4):

"""生成指定长度的验证码

:param code_len: 验证码的长度(默认4个字符)
:return: 由大小写英文字母和数字构成的随机验证码字符串
"""
return ''.join(random.choices(ALL_CHARS, k=code_len))

1
2
3
4
5
6
7
8
9
10
11
12
13
可以用下面的代码生成10组随机验证码来测试上面的函数。

for _ in range(10):

print(generate_code()) 

1
2
说明:random模块的sample和choices函数都可以实现随机抽样,sample实现无放回抽样,这意味着抽样取出的字符是不重复的;choices实现有放回抽样,这意味着可能会重复选中某些字符。这两个函数的第一个参数代表抽样的总体,而参数k代表抽样的数量。

案例2:设计一个函数返回给定文件的后缀名。
说明:文件名通常是一个字符串,而文件的后缀名指的是文件名中最后一个.后面的部分,也称为文件的扩展名,它是某些操作系统用来标记文件类型的一种机制,例如在Windows系统上,后缀名exe表示这是一个可执行程序,而后缀名txt表示这是一个纯文本文件。需要注意的是,在Linux和macOS系统上,文件名可以以.开头,表示这是一个隐藏文件,像.gitignore这样的文件名,.后面并不是后缀名,这个文件没有后缀名或者说后缀名为''。

def get_suffix(filename, ignore_dot=True):

"""获取文件名的后缀名

:param filename: 文件名
:param ignore_dot: 是否忽略后缀名前面的点
:return: 文件的后缀名
"""
# 从字符串中逆向查找.出现的位置
pos = filename.rfind('.')
# 通过切片操作从文件名中取出后缀名
if pos <= 0:
    return ''
return filename[pos + 1:] if ignore_dot else filename[pos:]

1
2
3
4
5
6
7
8
9
10
11
12
13
可以用下面的代码对上面的函数做一个简单的测验。

print(get_suffix('readme.txt')) # txt
print(get_suffix('readme.txt.md')) # md
print(get_suffix('.readme')) #
print(get_suffix('readme.')) #
print(get_suffix('readme')) #
1
2
3
4
5
上面的get_suffix函数还有一个更为便捷的实现方式,就是直接使用os.path模块的splitext函数,这个函数会将文件名拆分成带路径的文件名和扩展名两个部分,然后返回一个二元组,二元组中的第二个元素就是文件的后缀名(包含.),如果要去掉后缀名中的.,可以做一个字符串的切片操作,代码如下所示。

from os.path import splitext

def get_suffix(filename, ignore_dot=True):

return splitext(filename)[1][1:]

1
2
3
4
5
思考:如果要给上面的函数增加一个参数,用来控制文件的后缀名是否包含.,应该怎么做?

案例3:写一个判断给定的正整数是不是质数的函数。
def is_prime(num: int) -> bool:

"""判断一个正整数是不是质数

:param num: 正整数
:return: 如果是质数返回True,否则返回False
"""
for i in range(2, int(num ** 0.5) + 1):
    if num % i == 0:
        return False
return num != 1

1
2
3
4
5
6
7
8
9
10
案例4:写出计算两个正整数最大公约数和最小公倍数的函数。
代码一:

def gcd_and_lcm(x: int, y: int) -> int:

"""求最大公约数和最小公倍数"""
a, b = x, y
while b % a != 0:
    a, b = b % a, a
return a, x * y // a

1
2
3
4
5
6
代码二:

def gcd(x: int, y: int) -> int:

"""求最大公约数"""
while y % x != 0:
    x, y = y % x, x
return x

def lcm(x: int, y: int) -> int:

"""求最小公倍数"""
return x * y // gcd(x, y)

1
2
3
4
5
6
7
8
9
10
思考:请比较上面的代码一和代码二,想想哪种做法是更好的选择。

案例5:写出计算一组样本数据描述性统计信息的函数。
import math

def ptp(data):

"""求极差(全距)"""
return max(data) - min(data)

def average(data):

"""求均值"""
return sum(data) / len(data)

def variance(data):

"""求方差"""
x_bar = average(data)
temp = [(num - x_bar) ** 2 for num in data]
return sum(temp) / (len(temp) - 1)

def standard_deviation(data):

"""求标准差"""
return math.sqrt(variance(data))

def median(data):

"""找中位数"""
temp, size = sorted(data), len(data)
if size % 2 != 0:
    return temp[size // 2]
else:
    return average(temp[size // 2 - 1:size // 2 + 1])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

案例小总结
在写代码尤其是开发商业项目的时候,一定要有意识的将相对独立且重复出现的功能封装成函数,这样不管是自己还是团队的其他成员都可以通过调用函数的方式来使用这些功能。

函数使用进阶
前面我们讲到了关于函数的知识,我们还讲到过Python中常用的数据类型,这些类型的变量都可以作为函数的参数或返回值,用好函数还可以让我们做更多的事情。

关键字参数
下面是一个判断传入的三条边长能否构成三角形的函数,在调用函数传入参数时,我们可以指定参数名,也可以不指定参数名,代码如下所示。

def is_triangle(a, b, c):

print(f'a = {a}, b = {b}, c = {c}')
return a + b > c and b + c > a and a + c > b

调用函数传入参数不指定参数名按位置对号入座

print(is_triangle(1, 2, 3))

调用函数通过“参数名=参数值”的形式按顺序传入参数

print(is_triangle(a=1, b=2, c=3))

调用函数通过“参数名=参数值”的形式不按顺序传入参数

print(is_triangle(c=3, a=1, b=2))
1
2
3
4
5
6
7
8
9
10
11
在没有特殊处理的情况下,函数的参数都是位置参数,也就意味着传入参数的时候对号入座即可,如上面代码的第7行所示,传入的参数值1、2、3会依次赋值给参数a、b、c。当然,也可以通过参数名=参数值的方式传入函数所需的参数,因为指定了参数名,传入参数的顺序可以进行调整,如上面代码的第9行和第11行所示。

调用函数时,如果希望函数的调用者必须以参数名=参数值的方式传参,可以用命名关键字参数(keyword-only argument)取代位置参数。所谓命名关键字参数,是在函数的参数列表中,写在*之后的参数,代码如下所示。

def is_triangle(*, a, b, c):

print(f'a = {a}, b = {b}, c = {c}')
return a + b > c and b + c > a and a + c > b

TypeError: is_triangle() takes 0 positional arguments but 3 were given

print(is_triangle(3, 4, 5))

传参时必须使用“参数名=参数值”的方式,位置不重要

print(is_triangle(a=3, b=4, c=5))
print(is_triangle(c=5, b=4, a=3))
1
2
3
4
5
6
7
8
9
10
注意:上面的is_triangle函数,参数列表中的是一个分隔符,前面的参数都是位置参数,而*后面的参数就是命名关键字参数。

我们之前讲过在函数的参数列表中可以使用可变参数args来接收任意数量的参数,但是我们需要看看,args是否能够接收带参数名的参数。

def calc(*args):

result = 0
for arg in args:
    if type(arg) in (int, float):
        result += arg
return result

print(calc(a=1, b=2, c=3))
1
2
3
4
5
6
7
8
9
执行上面的代码会引发TypeError错误,错误消息为calc() got an unexpected keyword argument 'a',由此可见,*args并不能处理带参数名的参数。我们在设计函数时,如果既不知道调用者会传入的参数个数,也不知道调用者会不会指定参数名,那么同时使用可变参数和关键字参数。关键字参数会将传入的带参数名的参数组装成一个字典,参数名就是字典中键值对的键,而参数值就是字典中键值对的值,代码如下所示。

def calc(args, *kwargs):

result = 0
for arg in args:
    if type(arg) in (int, float):
        result += arg
for value in kwargs.values():
    if type(value) in (int, float):
        result += value
return result

print(calc()) # 0
print(calc(1, 2, 3)) # 6
print(calc(a=1, b=2, c=3)) # 6
print(calc(1, 2, c=3, d=4)) # 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
提示:不带参数名的参数(位置参数)必须出现在带参数名的参数(关键字参数)之前,否则将会引发异常。例如,执行calc(1, 2, c=3, d=4, 5)将会引发SyntaxError错误,错误消息为positional argument follows keyword argument,翻译成中文意思是“位置参数出现在关键字参数之后”。

高阶函数的用法
在前面几节课中,我们讲到了面向对象程序设计,在面向对象的世界中,一切皆为对象,所以类和函数也是对象。函数的参数和返回值可以是任意类型的对象,这就意味着函数本身也可以作为函数的参数或返回值,这就是所谓的高阶函数。

如果我们希望上面的calc函数不仅仅可以做多个参数求和,还可以做多个参数求乘积甚至更多的二元运算,我们就可以使用高阶函数的方式来改写上面的代码,将加法运算从函数中移除掉,具体的做法如下所示。

def calc(args, init_value, op, *kwargs):

result = init_value
for arg in args:
    if type(arg) in (int, float):
        result = op(result, arg)
for value in kwargs.values():
    if type(value) in (int, float):
        result = op(result, value)
return result

1
2
3
4
5
6
7
8
9
注意,上面的函数增加了两个参数,其中init_value代表运算的初始值,op代表二元运算函数。经过改造的calc函数不仅仅可以实现多个参数的累加求和,也可以实现多个参数的累乘运算,代码如下所示。

def add(x, y):

return x + y

def mul(x, y):

return x * y

print(calc(1, 2, 3, init_value=0, op=add, x=4, y=5)) # 15
print(calc(1, 2, x=3, y=4, z=5, init_value=1, op=mul)) # 120
1
2
3
4
5
6
7
8
9
10
通过对高阶函数的运用,calc函数不再和加法运算耦合,所以灵活性和通用性会变强,这是一种解耦合的编程技巧,但是最初学者来说可能会稍微有点难以理解。需要注意的是,将函数作为参数和调用函数是有显著的区别的,调用函数需要在函数名后面跟上圆括号,而把函数作为参数时只需要函数名即可。上面的代码也可以不用定义add和mul函数,因为Python标准库中的operator模块提供了代表加法运算的add和代表乘法运算的mul函数,我们直接使用即可,代码如下所示。

import operator

print(calc(1, 2, 3, init_value=0, op=operator.add, x=4, y=5)) # 15
print(calc(1, 2, x=3, y=4, z=5, init_value=1, op=operator.mul)) # 120
1
2
3
4
Python内置函数中有不少高阶函数,我们前面提到过的filter和map函数就是高阶函数,前者可以实现对序列中元素的过滤,后者可以实现对序列中元素的映射,例如我们要去掉一个整数列表中的奇数,并对所有的偶数求平方得到一个新的列表,就可以直接使用这两个函数来做到,具体的做法是如下所示。

def is_even(num):

return num % 2 == 0

def square(num):

return num ** 2

numbers1 = [35, 12, 8, 99, 60, 52]
numbers2 = list(map(square, filter(is_even, numbers1)))
print(numbers2) # [144, 64, 3600, 2704]
1
2
3
4
5
6
7
8
9
10
11
当然,要完成上面代码的功能,也可以使用列表生成式,列表生成式的做法更为简单优雅。

numbers1 = [35, 12, 8, 99, 60, 52]
numbers2 = [num ** 2 for num in numbers1 if num % 2 == 0]
print(numbers2) # [144, 64, 3600, 2704]
1
2
3
Lambda函数
在使用高阶函数的时候,如果作为参数或者返回值的函数本身非常简单,一行代码就能够完成,那么我们可以使用Lambda函数来表示。Python中的Lambda函数是没有的名字函数,所以很多人也把它叫做匿名函数,匿名函数只能有一行代码,代码中的表达式产生的运算结果就是这个匿名函数的返回值。上面代码中的is_even和square函数都只有一行代码,我们可以用Lambda函数来替换掉它们,代码如下所示。

numbers1 = [35, 12, 8, 99, 60, 52]
numbers2 = list(map(lambda x: x ** 2, filter(lambda x: x % 2 == 0, numbers1)))
print(numbers2) # [144, 64, 3600, 2704]
1
2
3
通过上面的代码可以看出,定义Lambda函数的关键字是lambda,后面跟函数的参数,如果有多个参数用逗号进行分隔;冒号后面的部分就是函数的执行体,通常是一个表达式,表达式的运算结果就是Lambda函数的返回值,不需要写return 关键字。

如果需要使用加减乘除这种简单的二元函数,也可以用Lambda函数来书写,例如调用上面的calc函数时,可以通过传入Lambda函数来作为op参数的参数值。当然,op参数也可以有默认值,例如我们可以用一个代表加法运算的Lambda函数来作为op参数的默认值。

def calc(args, init_value=0, op=lambda x, y: x + y, *kwargs):

result = init_value
for arg in args:
    if type(arg) in (int, float):
        result = op(result, arg)
for value in kwargs.values():
    if type(value) in (int, float):
        result = op(result, value)
return result

调用calc函数,使用init_value和op的默认值

print(calc(1, 2, 3, x=4, y=5)) # 15

调用calc函数,通过lambda函数给op参数赋值

print(calc(1, 2, 3, x=4, y=5, init_value=1, op=lambda x, y: x * y)) # 120
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
提示:注意上面的代码中的calc函数,它同时使用了可变参数、关键字参数、命名关键字参数,其中命名关键字参数要放在可变参数和关键字参数之间,传参时先传入可变参数,关键字参数和命名关键字参数的先后顺序并不重要。

有很多函数在Python中用一行代码就能实现,我们可以用Lambda函数来定义这些函数,调用Lambda函数就跟调用普通函数一样,代码如下所示。

import operator, functools

一行代码定义求阶乘的函数

fac = lambda num: functools.reduce(operator.mul, range(1, num + 1), 1)

一行代码定义判断素数的函数

is_prime = lambda x: x > 1 and all(map(lambda f: x % f, range(2, int(x ** 0.5) + 1)))

调用Lambda函数

print(fac(10)) # 3628800
print(is_prime(9)) # False
1
2
3
4
5
6
7
8
9
10
提示1:上面使用的reduce函数是Python标准库functools模块中的函数,它可以实现对数据的归约操作,通常情况下,过滤(filter)、映射(map)和归约(reduce)是处理数据中非常关键的三个步骤,而Python的标准库也提供了对这三个操作的支持。

提示2:上面使用的all函数是Python内置函数,如果传入的序列中所有布尔值都是True,all函数就返回True,否则all函数就返回False。

总结
Python中的函数可以使用可变参数args和关键字参数*kwargs来接收任意数量的参数,而且传入参数时可以带上参数名也可以没有参数名,可变参数会被处理成一个元组,而关键字参数会被处理成一个字典。Python中的函数是一等函数,可以赋值给变量,也可以作为函数的参数和返回值,这也就意味着我们可以在Python中使用高阶函数。如果我们要定义的函数非常简单,只有一行代码且不需要函数名,可以使用Lambda函数(匿名函数)。
————————————————
版权声明:本文为CSDN博主「五包辣条!」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/AI19970205/article/details/125295722

目录
相关文章
|
4天前
|
Python
python函数进阶
python函数进阶
|
3天前
|
安全 Python
Python量化炒股的获取数据函数—get_industry()
Python量化炒股的获取数据函数—get_industry()
11 3
|
4天前
|
Python
Python sorted() 函数和sort()函数对比分析
Python sorted() 函数和sort()函数对比分析
|
3天前
|
Python
Python量化炒股的获取数据函数—get_security_info()
Python量化炒股的获取数据函数—get_security_info()
10 1
|
7天前
|
数据库 开发者 Python
实战指南:用Python协程与异步函数优化高性能Web应用
在快速发展的Web开发领域,高性能与高效响应是衡量应用质量的重要标准。随着Python在Web开发中的广泛应用,如何利用Python的协程(Coroutine)与异步函数(Async Functions)特性来优化Web应用的性能,成为了许多开发者关注的焦点。本文将从实战角度出发,通过具体案例展示如何运用这些技术来提升Web应用的响应速度和吞吐量。
12 1
|
7天前
|
调度 Python
揭秘Python并发编程核心:深入理解协程与异步函数的工作原理
在Python异步编程领域,协程与异步函数成为处理并发任务的关键工具。协程(微线程)比操作系统线程更轻量级,通过`async def`定义并在遇到`await`表达式时暂停执行。异步函数利用`await`实现任务间的切换。事件循环作为异步编程的核心,负责调度任务;`asyncio`库提供了事件循环的管理。Future对象则优雅地处理异步结果。掌握这些概念,可使代码更高效、简洁且易于维护。
10 1
|
11天前
|
Python
[oeasy]python035_根据序号得到字符_chr函数_字符_character_
本文介绍了Python中的`ord()`和`chr()`函数。`ord()`函数通过字符找到对应的序号,而`chr()`函数则根据序号找到对应的字符。两者互为逆运算,可以相互转换。文章还探讨了单双引号在字符串中的作用,并解释了中文字符和emoji也有对应的序号。最后总结了`ord()`和`chr()`函数的特点,并提供了学习资源链接。
16 4
|
14天前
|
Java Python
全网最适合入门的面向对象编程教程:50 Python函数方法与接口-接口和抽象基类
【9月更文挑战第18天】在 Python 中,虽无明确的 `interface` 关键字,但可通过约定实现类似功能。接口主要规定了需实现的方法,不提供具体实现。抽象基类(ABC)则通过 `@abstractmethod` 装饰器定义抽象方法,子类必须实现这些方法。使用抽象基类可使继承结构更清晰、规范,并确保子类遵循指定的方法实现。然而,其使用应根据实际需求决定,避免过度设计导致代码复杂。
|
16天前
|
Python
全网最适合入门的面向对象编程教程:Python函数方法与接口-函数与方法的区别和lamda匿名函数
【9月更文挑战第15天】在 Python 中,函数与方法有所区别:函数是独立的代码块,可通过函数名直接调用,不依赖特定类或对象;方法则是与类或对象关联的函数,通常在类内部定义并通过对象调用。Lambda 函数是一种简洁的匿名函数定义方式,常用于简单的操作或作为其他函数的参数。根据需求,可选择使用函数、方法或 lambda 函数来实现代码逻辑。
|
3天前
|
Python
Python量化炒股的获取数据函数— get_billboard_list()
Python量化炒股的获取数据函数— get_billboard_list()
下一篇
无影云桌面