Python基础知识
Python里有多线程吗?
Python里的多线程是假的多线程。
Python解释器由于设计时有GIL全局锁,导致了多线程无法利用多核,只有一个线程在解释器中运行。
对于I/O密集型任务,Python的多线程能起到作用,但对于CPU密集型任务,Python的多线程几乎占不到任何优势,还有可能因为争夺资源而变慢。
对所有面向I/O的(会调用内建的操作系统C代码的)程序来说,GIL会在这个I/O调用之前被释放,以允许其它的线程在这个线程等待I/O的时候运行。
如果是纯计算的程序,没有 I/O 操作,解释器会每隔 100 次操作就释放这把锁,让别的线程有机会执行(这个次数可以通过 sys.setcheckinterval 来调整)如果某线程并未使用很多I/O 操作,它会在自己的时间片内一直占用处理器和GIL。
缓解GIL锁的方法:多进程和协程(协程也只是单CPU,但是能减小切换代价提升性能)
GIL (全局解释器锁 - Global Interpreter Lock):
- 是什么: CPython 解释器中的一个互斥锁 (mutex)。它的作用是保护对 Python 对象的访问,确保在任何给定时刻,只有一个线程能持有 GIL 并执行 Python 字节码。
- 关键: 它锁的是解释器执行字节码这个动作,而不是整个线程。
Python中列表和元组的区别?
- 列表是可变的,在创建之后可以对其进行任意的修改。
- 元组是不可变的,元组一旦创建,便不能对其进行更改,可以元组当作一个只读版本的列表。
- 元组无法复制。
- Python将低开销的较大的块分配给元组,因为它们是不可变的。对于列表则分配小内存块。与列表相比,元组的内存更小。当你拥有大量元素时,元组比列表快。
常用的深度学习框架有哪些,都是哪家公司开发的?
- PyTorch:Facebook 研究首选,灵活易用,调试方便,社区活跃。(Caffe:UC Berkeley)
- TensorFlow:Google 生产部署强大,生态完善,工具链丰富,跨平台能力强。(Keras:Google )
- MxNet:Amazon
- PaddlePaddle:百度
PyTorch动态图和TensorFlow静态图的区别?
PyTorch动态图:计算图的运算与搭建同时进行;其较灵活,易调节。
TensorFlow静态图:计算图先搭建图,后运算;其较高效,不灵活。
PyTorch 的计算图是随着你的 Python 代码一行一行执行时才被建立起来的(动态构建),所以计算流程和你的 Python 代码逻辑是紧密同步的。
这带来了巨大的调试优势:
- 错误定位准确: 如果计算中出现错误(比如维度不匹配、数值错误 NaN),错误发生的地方通常就是你 Python 代码中执行相应操作的那一行。Python 的错误回溯 (Traceback) 会直接指向你的源代码。
- 可使用标准 Python 工具: 你可以在任何一行 PyTorch 代码前设置一个断点(比如用
import pdb; pdb.set_trace()
或者 IDE 的断点功能)。当程序执行到断点时,它会暂停。 - 检查中间值: 在断点处,你可以像检查普通 Python 变量一样,直接查看任何 PyTorch 张量(Tensor)的当前值、形状、数据类型等。这对于理解计算过程中数据的变化、找出逻辑错误非常有帮助。
- 逐行执行: 你可以使用调试器逐行执行 PyTorch 代码,观察每一步操作后张量的变化,以及计算图是如何被构建的(虽然你看不到图本身,但能看到执行效果)。
对比静态图的调试困难:
在静态图框架中,你先定义整个图,然后启动执行。如果执行过程中出错,错误信息可能指向图内部的一个底层操作,很难直接关联回你定义图的那部分 Python 代码。你也不能轻易地在图的“中间”设置断点来检查运行时的中间张量值,因为图的执行可能已经脱离了 Python 解释器的直接控制,被优化并交给后端(如 C++ 或 CUDA)执行了。
Python中的可变对象和不可变对象?
可变对象与不可变对象的区别在于对象本身是否可变。
可变对象:list(列表) dict(字典) set(集合)
不可变对象:tuple(元组) string(字符串) int(整型) float(浮点型) bool(布尔型)
Python中None代表什么含义?
在 Python 中,None
是一个特殊的常量,表示 “没有值” 或 “空值” 。它通常用来表示以下几种场景:
- 一个函数没有显式返回值。
- 一个变量的值尚未被定义。
- 用来表示某种占位符意义的“无”值。
通过合理使用 None
,可以提高代码的可读性和鲁棒性。
Python中常见的切片操作
[:n]代表列表中的第一项到第n项。我们看一个例子:
1 | example = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
[n:]代表列表中第n+1项到最后一项:
1 | example = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
总结:为什么看起来 n
代表的项数不一样?
- 在
[:n]
中,n
定义了切片的结束边界(不包含)。因为索引从 0 开始,所以到索引n
之前正好是n
个元素(第 1 到第 n 项)。 - 在
[n:]
中,n
定义了切片的起始边界(包含)。因为索引从 0 开始,索引n
本身对应的是列表中的第n+1
个元素。
这种设定是 Python 切片设计的一部分,虽然初看可能有点绕,但它使得基于索引的操作非常一致和方便,例如:
list[:n] + list[n:]
总是等于原始的list
。len(list[:n])
总是等于n
(如果n
不超过列表长度)。
记住核心规则:start
包含,stop
不包含,索引从 0 开始。这样就能准确理解各种切片操作了。
[-1]代表取列表的最后一个元素:
1 | example = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
[:-1]代表取除了最后一个元素的所有元素:
1 | example = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
[::-1]代表取整个列表的相反列表:
1 | example = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
[1:]代表从第二个元素意指读取到最后一个元素:
1 | example = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
[4::-1]代表取下标为4(即第五个元素)的元素和之前的元素反转读取:
1 | example = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
Python中如何进行异常处理?
一般情况下,在Python无法正常处理程序时就会发生一个异常。异常在Python中是一个对象,表示一个错误。当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。
捕捉异常可以使用try,except和finally语句。
try和except语句用来检测try语句块中的错误,从而让except语句捕获异常信息并处理。
1 | try: |
Python中remove,del以及pop之间的区别?
remove,del以及pop都可以用于删除列表、字符串等里面的元素,但是具体用法并不相同。
- remove是剔除第一个匹配的值。
- del是通过索引来删除当中的元素。
- pop是通过索引来删除当中的元素,并且返回该元素;若括号内不添加索引值,则默认删除最后一个元素。
1 | >>> a = [0, 1, 2, 1, 3] |
python中如何无损打开图像,并无损保存图像?
在Python中,如果我们想无损地打开和保存图像,关键是选择支持无损保存的图像格式,如PNG、TIFF或BMP。使用Python的Pillow库可以方便地处理这种任务。
使用Pillow无损打开和保存图像
- 打开图像:使用
Image.open()
函数打开图像文件。这个函数不会修改图像数据,因此打开图像本身是无损的。 - 保存图像:使用
Image.save()
函数,并确保选择无损格式,如PNG。
以下是一个具体的示例:
1 | from PIL import Image |
注意事项
- PNG使用的是无损压缩算法,这意味着图像的所有信息在压缩和解压过程中都被完整保留,不会有任何质量损失。
- 当从有损压缩格式(如JPEG)转换为无损格式(如PNG)时,虽然保存过程是无损的,但原始从JPEG格式读取的图像可能已经丢失了一些信息。因此,最佳实践是始终从无损格式源文件开始操作,以保持最高质量。
- 如果我们的图像已经在一个无损格式(如PNG),你只需重新保存它,同样选择无损格式,即可保持图像质量不变。
- 无损保存与
optimize=True
选项:optimize=True
选项会尝试找到更为压缩的存储方式来保存文件,但不会影响图像的质量。具体来说,它在保存文件时会尝试优化图像数据的存储方式,比如重新排列文件中的色块和使用更有效的编码方法,但仍然保持图像数据的完整性和质量。因此,即使使用了optimize=True
选项,保存的PNG文件也是无损的,不会有质量损失。这使得PNG格式非常适合需要无损压缩的应用,如需要频繁编辑和保存的图像处理任务。
处理其他图像格式
对于其他格式如TIFF或BMP,Pillow同样支持无损操作,保存方法类似,只需改变保存格式即可:
1 | # 保存为TIFF格式 |
使用Pillow库,我们可以轻松地在Python中进行无损图像处理。它提供了广泛的功能,能够处理几乎所有常见的图像格式,并支持复杂的图像处理任务。
Python多进程中的fork和spawn模式有什么区别?
- windows和MacOS中默认为spawn模式,unix系统默认为fork模式,其中windows只支持spawn,unix同时支持两者;
- spawn模式不会继承父进程的资源,而是从头创建一个全新的进程,启动较慢;
- fork模式会继承父进程的资源,即通过复制父进程资源来快速创建子进程,启动较快;
什么是Python中的推导式?Python的推导式一共有多少种?
Python中的推导式(comprehensions)是一种简洁、灵活且高效的构建Python数据结构的方法,包括列表、字典、集合和生成器。推导式允许以表达式的形式快速生成新的数据结构,同时在创建过程中可以直接应用条件筛选或操作。下面详细介绍Python中四种主要的推导式:
1. 列表推导式(List Comprehensions)
功能:用于创建列表,可以通过应用表达式自动处理并生成新列表。
基本语法:
1 | [expression for item in iterable if condition] |
expression
是对item
的操作或者应用表达式。item
是从iterable
中逐个取出的元素。condition
是一个可选的条件语句,用于过滤。
示例:
1 | # 生成0-9每个数字的平方的列表 |
2. 字典推导式(Dictionary Comprehensions)
功能:用于创建字典,允许通过迭代可迭代对象来生成键值对。
基本语法:
1 | {key_expression : value_expression for item in iterable if condition} |
key_expression
表示字典的键的表达式。value_expression
表示字典的值的表达式。item
是从iterable
中逐个取出的元素。condition
是一个可选的条件语句,用于过滤。
示例:
1 | # 使用数字作为键,其平方作为值 |
3. 集合推导式(Set Comprehensions)
功能:用于创建集合,类似于列表推导式,但结果是一个集合,自动去重。
基本语法:
1 | {expression for item in iterable if condition} |
expression
是对item
的操作或者应用表达式。item
是从iterable
中逐个取出的元素。condition
是一个可选的条件语句,用于过滤。
示例:
1 | # 创建一个包含0-9每个数字平方的集合 |
4. 生成器推导式(Generator Expressions)
功能:生成器推导式是一种类似于列表推导式的结构,用于创建生成器(一种迭代器),不会一次性生成所有元素,而是按需产生,节约内存。
基本语法:
1 | (expression for item in iterable if condition) |
expression
是对item
的操作或者应用表达式。item
是从iterable
中逐个取出的元素。condition
是一个可选的条件语句,用于过滤。
示例:
1 | # 创建一个生成器,包含0-9每个数字的平方 |
推导式提供了一种高效和直观的方式来创建数据结构,使代码更加简洁易读。在合适的情况下,使用推导式可以有效提升编程效率和执行性能。
Python中函数传参时会改变参数本身吗?
在Python中,函数传参是否会改变参数本身取决于参数的数据类型和传递方式。这涉及到Python的参数传递机制,通常被称为“传引用的值”(pass-by-reference value)或者“传对象引用”(pass-by-object-reference)。这里的一些基本规则和示例将帮助大家理解这一概念:
可变与不可变对象
不可变对象:包括整数、浮点数、字符串、元组等。这些类型的数据不允许被修改。
- 当我们传递一个不可变对象给函数时,虽然函数内部可以使用这个对象的值,但任何试图改变该对象的操作都将在本地创建一个新对象。外部原始对象不会被改变。
1
2
3
4
5
6
7def modify(x):
x = 10
return x
a = 5
modify(a)
print(a) # 输出 5,原始值未改变可变对象:包括列表、字典、集合等。这些类型的数据可以被修改。
- 当你传递一个可变对象给函数时,函数内部对这个对象的任何修改都会反映到原始对象上。
1
2
3
4
5
6def modify(lst):
lst.append(3)
my_list = [1, 2]
modify(my_list)
print(my_list) # 输出 [1, 2, 3],原始列表被修改
函数参数的工作方式
- 在Python中,所有的函数参数都是按“引用传递”的。但实际上,这意味着当对象传递给函数时,传递的是对象的引用(内存地址),而不是对象的实际拷贝。对于不可变对象,由于不能被改变,所以任何修改都会导致创建一个新的本地对象;而对于可变对象,则可以在原地址上进行修改。
- 对于列表和字典这样的可变对象,如果你不希望函数中的操作影响到原始数据,你可以传递一个拷贝给函数,而不是原始对象本身。
1 | import copy |
总结
在Python中,函数的行为取决于传入参数的类型。不可变对象(如整数、字符串和元组)不会在函数调用中被修改,而可变对象(如列表和字典)可以被修改。了解这些差异有助于我们更好地管理函数中数据的状态和行为。
什么是python的全局解释器锁GIL?
在Python中,全局解释器锁(Global Interpreter Lock,简称GIL)是一个重要的概念,特别是在涉及多线程执行时。GIL 是一个互斥锁,保证同一时间内只有一个线程可以执行Python字节码。简而言之,尽管在多核处理器上运行,Python 的标准实现 CPython 在执行多线程应用时,并不能有效地利用多核处理器的优势。
GIL 的目的
- 简化内存管理:CPython 使用引用计数来管理内存,这种方法在多线程环境中容易产生问题。GIL 通过确保一次只有一个线程运行,避免了常见的并发访问问题,如竞态条件。
- 保护CPython的内部数据结构:没有GIL,程序员必须采用其他并发控制技术,如细粒度锁,这可能会使CPython的实现更复杂。
GIL 的影响
尽管GIL简化了内存管理和内部数据结构的保护,但它也限制了Python程序在多核处理器上的并行执行能力:
- 多线程局限性:在CPU密集型程序中,GIL成为性能瓶颈,因为线程不能在多个CPU核心上同时执行计算任务。
- I/O密集型应用的表现更好:I/O操作不需要大量CPU计算,线程可能会在等待I/O操作完成时释放GIL,从而让其他线程有机会执行。
绕过GIL
虽然GIL在多线程编程中存在局限性,但Python社区提供了多种方法来绕过这些限制:
- 使用多进程:通过
multiprocessing
模块,可以创建多个进程,每个进程拥有自己的Python解释器和内存空间,从而不受GIL的限制。 - 使用其他实现:如Jython和IronPython,这些Python实现没有GIL,可以更好地利用多核处理器。
- 使用特定库:一些库设计可以在底层进行多线程或多进程操作,从而绕过GIL的限制,例如NumPy和其他与C语言库交互的扩展。
什么是python的字符串格式化技术?
在Python中,字符串格式化是一项重要的技能,能帮助我们高效地生成和处理字符串。Python提供了多种字符串格式化的方法,包括旧式的百分号(%
)格式化、新式的str.format()
方法以及最新的f-string(格式化字符串字面量)。
1. 百分号(%)格式化
这是Python中最古老的字符串格式化方法,使用%
符号进行占位符替换。
基本用法:
1 | name = "Alice" |
%s
用于字符串%d
用于整数%f
用于浮点数
控制浮点数精度:
1 | pi = 3.14159 |
2. str.format()
方法
str.format()
方法更加灵活和强大,允许指定占位符的位置和格式。
基本用法:
1 | name = "Alice" |
使用索引指定占位符的位置:
1 | formatted_string = "Name: {0}, Age: {1}".format(name, age) |
使用命名参数:
1 | formatted_string = "Name: {name}, Age: {age}".format(name=name, age=age) |
控制浮点数精度:
1 | pi = 3.14159 |
3. f-string(格式化字符串字面量)
f-string是Python 3.6引入的一种新的格式化方法,提供了简洁和高效的方式。
基本用法:
1 | name = "Alice" |
控制浮点数精度:
1 | pi = 3.14159 |
字符串格式化技术的应用场景
- 日志记录:格式化日志信息以便调试和分析。
- 数据输出:生成报告或导出数据。
- 用户界面:动态显示信息。
Python中is和==的区别
Python 中,对于任意的变量都具有三个基本要素:分别是 id,type,value。其中 id 为身份标识,即唯一能识别变量的标志,type 为数据类型,value 为数据值。在定义变量之后,可以看到这几个基本要素:
1 | >>> a = 1 |
id(): 在Python中变量创建的时候,会为其分配一个内存地址,id()返回的是变量的内存地址。
is比较的是对象,即id(),==比较的是值
在Python中,整型对象和字符串对象是不可变对象,Python 会很高效地对它们进行缓存,这两个类型变量具有相同value时,id也是相等的。
1 | >>> a = 1 |
Python中type()和isinstance()的区别?
type() 函数用于获取对象的类型,返回对象的类型对象。它还可以用于动态创建类。
1 | # 获取对象类型 |
这行代码展示了 type()
函数的第二种、也是更高级的用法:动态地创建类 (class)。
通常我们使用 type(object)
来获取一个对象的类型(就像你的第一个例子 type(x)
)。但是,当 type()
接收三个参数时,它的作用就变成了创建一个新的类对象。
这三个参数分别是:
name
(字符串): 新创建的类的名字。- 在这个例子中是
'MyDynamicClass'
。这相当于你在用class
关键字定义类时写下的名字:class MyDynamicClass:
。
- 在这个例子中是
bases
(元组): 新创建的类所继承的父类(基类)的元组。- 在这个例子中是
()
,一个空元组。这表示MyDynamicClass
不继承任何特定的父类,它将默认继承自 Python 的根类object
(在 Python 3 中,即使省略,类也默认继承自object
)。这相当于class MyDynamicClass:
或class MyDynamicClass(object):
。 - 如果想继承其他类,比如
BaseClass1
和BaseClass2
,这里会写成(BaseClass1, BaseClass2)
,相当于class MyDynamicClass(BaseClass1, BaseClass2):
。
- 在这个例子中是
dict
(字典): 包含新创建类的属性和方法的字典。字典的键是属性名或方法名(字符串),值是对应的属性值或函数对象。- 在这个例子中是
{'x': 42}
。这表示MyDynamicClass
类将拥有一个名为x
的类属性,其值为整数42
。这相当于在类定义内部写x = 42
:
- 在这个例子中是
- type() 返回对象的类型对象,例如 <class ‘int’>。
- type() 主要用于获取对象的类型,以及在动态创建类时使用。
- type() 不考虑继承关系,仅比较确切的类型。 isinstance() 函数用于判断一个对象是否是一个已知的类型,返回 True 或 False。
1 | # 判断对象类型 |
- isinstance() 返回布尔值,表示对象是否是指定类型或类型元组中任意类型的实例。
- isinstance() 主要用于判断对象是否是指定类型,适用于检查对象是否属于某一类或其子类。
- isinstance() 考虑继承关系,如果对象是指定类型或其子类的实例,返回 True
Python中switch-case语句的实现?
在Python3.10中引入了新的match-case语法,它是一种用于模式匹配的结构。它类似于 switch-case 语句,可以根据不同的模式匹配执行不同的代码块。
- 常量匹配
1 | match x: |
- 变量匹配
1 | match x: |
- 类型匹配
1 | match value: |
- 结构化匹配
1 | class Point: |
- 区间匹配
1 | match value: |
case 后面的模式使用了区间表示。0..10 表示闭区间,包括 0 和 10;11..20 同样是闭区间,包括 11 和 20。如果匹配成功,相应的代码块将被执行。
需要注意的是,在区间匹配中,左边界必须小于或等于右边界。如果不满足这个条件,将会引发 SyntaxError 错误。
区间匹配也可以与其他类型的匹配结合使用
1 | match value: |
示例中,首先匹配原始值的类型,然后再根据整数值的区间进行匹配
match-case和switch-case的不同:
- 模式匹配:match-case 结构支持更灵活的模式匹配,可以匹配常量、变量、类型、结构化数据以及区间。这使得在匹配逻辑更加清晰,并且可以消除大量的连续的 if-elif 语句。
- 穿透:在 switch-case 语句中,一旦匹配到某个 case,默认会从匹配的 case 开始执行代码块,并且在每个 case 结束后终止整个 switch 结构。而在 match-case 结构中,默认是不会穿透的,也就是说只会执行匹配成功的 case 对应的代码块,并在执行完后立即退出 match-case 结构,不会执行其他 case 对应的代码块。
- 缺省情况:在 match-case 结构中可以使用 _ 作为默认模式,用于处理无法匹配到其他模式的情况。而在 switch-case 结构中,如果没有匹配到任何 case,需要自己另外处理这种情况。
- 可迭代对象:在 match-case 结构中,可以使用 match 对可迭代对象进行解构匹配,匹配其中的每个元素。而在 switch-case 结构中,需要手动遍历可迭代对象进行匹配。
介绍一下Python中耦合和解耦的代码设计思想
在AI行业的Python使用中,耦合和解耦的思想是设计良好的AI算法系统的重要原则。耦合(coupling)指的是模块或组件之间的依赖关系,而解耦(decoupling)指的是减少或消除这种依赖性,使AI算法系统的各部分可以独立开发、测试和维护。 下面是Python中耦合和解耦的详细方法(使用依赖注入、接口和抽象类、事件驱动架构等),提高AI算法系统的灵活性、可维护性和可扩展性。
1. 耦合(Coupling)
耦合表示不同模块或组件之间的依赖关系。当两个模块高度耦合时,一个模块的变化可能会影响另一个模块,导致系统维护和扩展的难度增加。耦合有两种主要形式:紧耦合和松耦合。
紧耦合
紧耦合是指模块之间的依赖性很强,任何一个模块的变化都会导致其他模块的变化。紧耦合系统难以维护和扩展。
示例:
1 | class Database: |
在这个示例中,UserService
直接依赖于 Database
,这使得它们高度耦合。如果 Database
类发生变化,UserService
也需要相应地修改。
2. 解耦(Decoupling)
解耦指的是减少或消除模块或组件之间的依赖关系,使它们能够独立地开发、测试和维护。解耦可以通过以下几种方法实现:
依赖注入(Dependency Injection)
依赖注入(松耦合)是一种设计模式,允许将依赖项从外部传递给一个对象,而不是在对象内部创建依赖项。
示例:
1 | class Database: |
在这个示例中,UserService
不再直接创建 Database
实例,而是通过构造函数接收一个 Database
实例。这减少了模块之间的耦合度。
使用接口和抽象类
通过使用接口或抽象类,可以将具体实现与接口分离,从而实现解耦。
示例:
1 | from abc import ABC, abstractmethod |
在这个示例中,Database
实现了 DatabaseInterface
接口,UserService
依赖于 DatabaseInterface
而不是 Database
的具体实现。这种方式提高了系统的灵活性和可维护性。
- 从设计角度看,
UserService
的编写是面向接口编程的,它只关心传入的对象是否满足DatabaseInterface
定义的契约。 - 从类型系统(通过类型提示)看,
UserService
明确要求一个符合DatabaseInterface
类型的对象。 - 这使得
UserService
不依赖于任何特定的实现细节,从而实现了与具体数据库实现的解耦。
虽然运行时执行的是 Database
类中的代码,但这种设计上的依赖关系是建立在抽象接口 DatabaseInterface
之上的。
使用事件驱动架构
事件驱动架构通过事件和消息来解耦模块。模块通过事件总线进行通信,而不需要直接依赖其他模块。
示例:
1 | class EventBus: |
在这个示例中,UserService
通过事件总线 EventBus
进行通信,而不是直接依赖其他模块。这种架构提高了系统的模块化和扩展性。
Python中的函数参数有哪些类型与规则?
在Python中,函数的参数有多种类型和一套设定的规则需要遵守,这使得函数定义和调用非常灵活。以下是Python详细的参数规则和类型解释:
1. 位置参数(Positional Arguments)
位置参数是最常见的参数类型,按顺序传递给函数。
1 | def greet(name, age): |
2. 关键字参数(Keyword Arguments)
关键字参数允许在函数调用时通过参数名指定参数值,使得参数传递更具可读性,并且不必按顺序传递。
1 | greet(age=30, name="Alice") |
3. 默认参数(Default Arguments)
默认参数在函数定义时指定默认值,如果在函数调用时未提供该参数,则使用默认值。
1 | def greet(name, age=25): |
4. 可变位置参数(Variable Positional Arguments)
使用 *args
语法,允许函数接受任意数量的位置参数。 *args
是一个元组。
1 | def greet(*names): |
5. 可变关键字参数(Variable Keyword Arguments)
使用 **kwargs
语法,允许函数接受任意数量的关键字参数。 **kwargs
是一个字典。
1 | def greet(**kwargs): |
参数顺序规则
在定义函数时,参数应按照以下顺序排列:
- 位置参数
- 关键字参数
- 默认参数
- 可变位置参数
*args
- 可变关键字参数
**kwargs
示例
1 | def example(a, b=2, *args, **kwargs): |
什么是Python中的魔术方法?
在Python类中,以双下划线(__
)开头和结尾的方法通常被称为“魔术方法”或“特殊方法”。这些方法定义了类的特殊行为,使类可以与Python的内置操作和函数紧密集成。以下是一些常见且常用的魔术方法:
1. 对象初始化和表示
__init__(self, ...)
:初始化对象时调用的构造方法。1
2
3class MyClass:
def __init__(self, value):
self.value = value__repr__(self)
:返回对象的官方字符串表示,通常可以用来重新创建该对象。1
2
3class MyClass:
def __repr__(self):
return f"MyClass({self.value!r)"__str__(self)
:返回对象的非正式字符串表示,适合用户友好输出。1
2
3class MyClass:
def __str__(self):
return f"Value is {self.value}"
2. 运算符重载
__add__(self, other)
:定义加法运算符+
的行为。1
2
3
4
5
6class MyClass:
def __init__(self, value):
self.value = value
def __add__(self, other):
return MyClass(self.value + other.value)__sub__(self, other)
:定义减法运算符-
的行为。1
2
3class MyClass:
def __sub__(self, other):
return MyClass(self.value - other.value)__mul__(self, other)
:定义乘法运算符*
的行为。1
2
3class MyClass:
def __mul__(self, other):
return MyClass(self.value * other.value)__truediv__(self, other)
:定义真除法运算符/
的行为。1
2
3class MyClass:
def __truediv__(self, other):
return MyClass(self.value / other.value)
3. 比较运算符
__eq__(self, other)
:定义等于运算符==
的行为。1
2
3class MyClass:
def __eq__(self, other):
return self.value == other.value__lt__(self, other)
:定义小于运算符<
的行为。1
2
3class MyClass:
def __lt__(self, other):
return self.value < other.value__gt__(self, other)
:定义大于运算符>
的行为。1
2
3class MyClass:
def __gt__(self, other):
return self.value > other.value
4. 容器类型协议
__len__(self)
:定义len()
函数的行为。1
2
3class MyClass:
def __len__(self):
return len(self.value)__getitem__(self, key)
:定义获取元素的行为,如self[key]
。1
2
3class MyClass:
def __getitem__(self, key):
return self.value[key]__setitem__(self, key, value)
:定义设置元素的行为,如self[key] = value
。1
2
3class MyClass:
def __setitem__(self, key, value):
self.value[key] = value__delitem__(self, key)
:定义删除元素的行为,如del self[key]
。1
2
3class MyClass:
def __delitem__(self, key):
del self.value[key]
5. 迭代器协议
__iter__(self)
:定义返回迭代器的行为。1
2
3class MyClass:
def __iter__(self):
return iter(self.value)__next__(self)
:定义迭代器的下一个元素。1
2
3class MyClass:
def __next__(self):
return next(self.value)
6. 可调用对象
__call__(self, ...)
:使对象可以像函数一样被调用。1
2
3class MyClass:
def __call__(self, *args, **kwargs):
print("Called with", args, kwargs)
这些魔术方法使得类在使用时更加灵活和自然,能够与Python内置的操作和函数无缝衔接。
介绍一下Python中常用的标准库以及功能
Python 提供了丰富的标准库,这些库为我们提供了常用的工具和功能,涵盖了从操作系统交互、文件处理、数据序列化、网络通信到多线程编程等方方面面。这些标准库大大简化了我们的工作,使得开发高效、稳定、易于维护的应用程序变得更加容易。在实际项目中,熟练掌握和合理运用这些标准库,可以显著提高我们的开发效率和代码质量。
1. os
- 功能:
os
模块提供了一种与操作系统进行交互的便捷方式。我们可以使用它来处理文件和目录、管理环境变量、执行操作系统命令等。 - 常用功能:
os.path
: 用于路径操作(如路径拼接、文件名提取)。os.makedirs()
: 创建多层目录。os.getenv()
: 获取环境变量。os.system()
: 执行系统命令。
2. sys
- 功能:
sys
模块提供了与 Python 解释器相关的函数和变量,允许我们与解释器进行交互。 - 常用功能:
sys.argv
: 命令行参数列表。sys.exit()
: 终止程序运行。sys.path
: 模块搜索路径列表,可以动态修改。sys.stdout
/sys.stderr
: 输出流和错误流的重定向。
3. math
- 功能:
math
模块提供了基本的数学函数和常量,如三角函数、对数、指数、平方根、常数(如π)等。 - 常用功能:
math.sqrt()
: 计算平方根。math.sin()
,math.cos()
,math.tan()
: 三角函数。math.log()
: 计算对数(自然对数和其他基数对数)。math.factorial()
: 计算阶乘。
4. datetime
- 功能:
datetime
模块用于处理日期和时间,支持日期的算术运算、格式化、解析等操作。 - 常用功能:
datetime.date
: 表示日期(年、月、日)。datetime.time
: 表示时间(时、分、秒、毫秒)。datetime.datetime
: 表示日期和时间的组合。datetime.timedelta
: 表示两个日期或时间的差。datetime.strftime()
: 格式化日期和时间为字符串。datetime.strptime()
: 从字符串解析日期和时间。
5. time
- 功能:
time
模块提供了与时间相关的函数,如暂停、获取当前时间等。 - 常用功能:
time.time()
: 返回当前时间的时间戳(自1970-01-01以来的秒数)。time.sleep()
: 让程序暂停指定的时间(秒)。time.localtime()
: 将时间戳转换为本地时间的结构体。time.strftime()
: 格式化时间为字符串。
6. random
- 功能:
random
模块用于生成伪随机数,并提供了随机选择、打乱顺序等功能。 - 常用功能:
random.random()
: 返回0到1之间的随机浮点数。random.randint(a, b)
: 返回a到b之间的随机整数。random.choice()
: 从序列中随机选择一个元素。random.shuffle()
: 随机打乱序列顺序。random.sample()
: 从序列中随机取样。
7. re
- 功能:
re
模块提供了正则表达式的支持,允许你在字符串中进行复杂的模式匹配、查找和替换。 - 常用功能:
re.match()
: 从字符串的起始位置进行匹配。re.search()
: 在字符串中查找模式的首次出现。re.findall()
: 查找字符串中所有符合模式的部分。re.sub()
: 替换字符串中符合模式的部分。
8. json
- 功能:
json
模块提供了将 Python 对象转换为 JSON 格式,以及将 JSON 数据解析为 Python 对象的功能。 - 常用功能:
json.dump()
: 将 Python 对象序列化为 JSON 格式,并写入文件。json.dumps()
: 将 Python 对象序列化为 JSON 格式的字符串。json.load()
: 从文件中读取 JSON 数据并解析为 Python 对象。json.loads()
: 将 JSON 字符串解析为 Python 对象。
9. subprocess
- 功能:
subprocess
模块允许我们生成子进程,并与其交互,代替旧的os.system()
方法。 - 常用功能:
subprocess.run()
: 运行命令并等待其完成。subprocess.Popen()
: 启动一个子进程,并可以通过stdin
,stdout
,stderr
与其交互。subprocess.call()
: 执行命令并返回状态码。
10. collections
- 功能:
collections
模块提供了几个有用的容器数据类型,如Counter
,deque
,defaultdict
,namedtuple
等。 - 常用功能:
Counter
: 用于计数的字典,可以统计元素出现的次数。deque
: 双端队列,支持在两端高效地添加和删除元素。defaultdict
: 带有默认值的字典。namedtuple
: 定义命名元组,可以像对象一样访问元素。
11. itertools
- 功能:
itertools
模块提供了用于操作迭代器的函数,用于高效地处理循环和组合生成器等任务。 - 常用功能:
itertools.chain()
: 将多个迭代器连接在一起。itertools.cycle()
: 无限循环一个迭代器。itertools.permutations()
: 生成序列的所有排列。itertools.combinations()
: 生成序列的所有组合。itertools.product()
: 生成笛卡尔积。
12. functools
- 功能:
functools
模块提供了处理和操作函数的工具,支持部分函数应用、缓存、比较等功能。 - 常用功能:
functools.partial()
: 创建一个部分应用的函数。functools.lru_cache()
: 通过缓存来优化函数性能。functools.reduce()
: 累积地将函数应用于序列的元素。
13. threading
- 功能:
threading
模块支持多线程编程,允许我们在 Python 中创建和管理线程。 - 常用功能:
threading.Thread()
: 创建并启动一个新线程。threading.Lock()
: 实现线程间的互斥锁。threading.Event()
: 用于线程间通信的同步原语。threading.Timer()
: 延迟执行的线程。
14. multiprocessing
- 功能:
multiprocessing
模块提供了支持并行处理的功能,通过在多个进程中分配任务来提高计算效率。 - 常用功能:
multiprocessing.Process()
: 创建并启动一个新进程。multiprocessing.Pool()
: 创建一个进程池,用于并行处理多个任务。multiprocessing.Queue()
: 用于进程间通信的队列。multiprocessing.Manager()
: 管理共享状态的服务。
15. shutil
- 功能:
shutil
模块提供了高级的文件操作功能,如复制、移动、删除文件和目录。 - 常用功能:
shutil.copy()
: 复制文件。shutil.move()
: 移动文件或目录。shutil.rmtree()
: 删除目录及其所有内容。shutil.make_archive()
: 创建压缩文件(zip、tar 等)。
16. glob
- 功能:
glob
模块用于匹配文件路径名模式,如查找符合特定模式的文件。 - 常用功能:
glob.glob()
: 返回符合特定模式的文件路径列表。glob.iglob()
: 返回一个迭代器,生成符合模式的文件路径。
17. csv
- 功能:
csv
模块提供了读写 CSV 文件的功能,支持多种格式的 CSV 文件操作。 - 常用功能:
csv.reader()
: 读取 CSV 文件内容,返回一个可迭代的 reader 对象。csv.writer()
: 写入 CSV 文件内容。csv.DictReader()
: 以字典的形式读取 CSV 文件。csv.DictWriter()
: 以字典的形式写入 CSV 文件。
18. hashlib
- 功能:
hashlib
模块提供了用于生成哈希值和摘要的算法,如 SHA-1、SHA-256、MD5 等。 - 常用功能:
hashlib.sha256()
: 生成 SHA-256 哈希值。hashlib.md5()
: 生成 MD5 哈希值。hashlib.blake2b()
: 生成 Blake2b 哈希值。hashlib.sha512()
: 生成 SHA-512 哈希值。
19. http
- 功能:
http
模块提供了处理 HTTP 请求和响应的功能,包含服务器和客户端相关的工具。 - 常用功能:
http.client
: 用于发起 HTTP 请求。http.server
: 用于创建简单的 HTTP 服务器。http.cookies
: 用于处理 HTTP Cookies。http.HTTPStatus
: 枚举 HTTP 状态码。
20. socket
- 功能:
socket
模块提供了低级别的网络通信接口,支持 TCP、UDP、IP 等网络协议的编程。 - 常用功能:
socket.socket()
: 创建一个套接字对象。socket.bind()
: 绑定套接字到地址。socket.listen()
: 监听连接。socket.accept()
: 接受连接请求。socket.connect()
: 连接到远程套接字。
python中海象运算符的介绍
介绍
Python 3.8 引入了赋值表达式(也称为海象运算符),它允许在表达式中进行赋值操作。赋值表达式的基本语法是 :=
,它将右侧的值赋给左侧的变量,并返回该值。
语法
1 | <variable> := <expression> |
示例
基本用法
1 | if (n := len(a)) > 10: |
循环中
1 | while (line := file.readline()) != '': |
函数参数
1 | def send_email(address, /, *, subject, message, sender): |
赋值表达式可以简化代码,特别是在需要计算和赋值的情况下。