1. Python 直接赋值、浅拷贝和深度拷贝解析
-
直接赋值:其实就是对象的引用(别名)。
-
浅拷贝(copy):拷贝父对象,不会拷贝对象的内部的子对象。
-
深拷贝(deepcopy): copy 模块的 deepcopy 方法,完全拷贝了父对象及其子对象。
- 不可变对象类型(整数、浮点数、字符串、元组),没有被拷贝的说法,即便是用深拷贝,查看id的话也是一样的,如果对其重新赋值,也只是新创建一个对象,替换掉旧的而已
2. 可迭代对象、迭代器和生成器
2.1 可迭代对象
定义:
- 一个可迭代对象是实现了
__iter__()
方法。__iter__()
方法返回一个迭代器。 - 常见的可迭代对象包括列表、元组、字典、集合和字符串等。
特点:
- 可迭代对象可以被用在
for
循环中,通过逐一访问其元素。 - 可以使用
iter()
函数将可迭代对象转换为迭代器。
注意:能用 for 循环使用的对象,不一定是可迭代对象
from collections import Iterable, Iterator, Generator
class IterObj:
def __init__(self):
self.a = [3, 5, 7, 11, 13, 17, 19]
def __getitem__(self, i):
return self.a[i]
it = IterObj()
print(isinstance(it, Iterable)) # False
print(isinstance(it, Iterator)) # False
print(isinstance(it, Generator)) # False
print(hasattr(it, "__iter__")) # False
print(iter(it)) # <iterator object at 0x10b231278>
for i in it:
print(i) # 将打印出3、5、7、11、13、17、19
2.2 迭代器
定义:
- 迭代器是实现了迭代器协议的对象,具体来说,就是实现了
__iter__()
和__next__()
方法的对象。 __iter__()
方法返回迭代器对象本身,__next__()
方法返回下一个元素,如果没有元素可返回,则抛出StopIteration
异常。
特点:
- 迭代器用于逐个访问可迭代对象的元素,通常只遍历一次。
- 可以使用
next()
函数获取迭代器的下一个元素。
2.3. 生成器
定义:
- 生成器是使用
yield
关键字的函数,返回一个生成器对象,该对象是 Python 的迭代器。
特性:
- 生成器是简化迭代器书写的语法糖。
- 每次执行到
yield
时,生成器会返回一个值,并记住函数执行的状态(局部变量、指令指针等),在下次迭代时从暂停处继续执行。 - 生成器非常适合处理大量数据或无限序列,因为它们在每次迭代时才生成数据,不会一次性将所有数据加载到内存中。
3. 正则表达式的match方法和search方法有什么区别?
3.1 re.match
方法
- 功能:
re.match
尝试从字符串的起始位置进行匹配。 - 返回值: 如果匹配成功,返回一个
Match
对象;否则返回None
。 - 使用场景: 当你希望检查字符串是否以某个模式开头时,可以使用
re.match
。
3.2 re.search
方法
- 功能:
re.search
扫描整个字符串,并返回第一个成功匹配的位置。 - 返回值: 如果匹配成功,返回一个
Match
对象;否则返回None
。 - 使用场景: 当你希望在整个字符串中搜索某个模式时,可以使用
re.search
。
4. 类变量
在 Python 中,类变量是类级别的属性,它们在类的所有实例之间共享。类变量在类定义体中定义,而实例变量在 __init__
方法中定义。类变量在所有实例中共享相同的值,而实例变量是每个实例独有的。
4.1 类变量和实例变量的区别
- 类变量:在类定义体中定义,所有实例共享相同的值。
- 实例变量:在
__init__
方法中定义,每个实例独有。
class MyClass:
# 类变量
class_variable = "I am a class variable"
def __init__(self, instance_variable):
# 实例变量
self.instance_variable = instance_variable
# 创建类的实例
obj1 = MyClass("I am instance variable 1")
obj2 = MyClass("I am instance variable 2")
# 访问类变量
print(MyClass.class_variable) # 输出: I am a class variable
print(obj1.class_variable) # 输出: I am a class variable
print(obj2.class_variable) # 输出: I am a class variable
# 访问实例变量
print(obj1.instance_variable) # 输出: I am instance variable 1
print(obj2.instance_variable) # 输出: I am instance variable 2
# 修改类变量
MyClass.class_variable = "New value"
print(obj1.class_variable) # 输出: New value
print(obj2.class_variable) # 输出: New value
# 修改实例变量
obj1.instance_variable = "Changed instance variable 1"
print(obj1.instance_variable) # 输出: Changed instance variable 1
print(obj2.instance_variable) # 输出: I am instance variable 2
4.2 类变量的继承
- 子类会继承父类的类变量。
- 当子类修改类变量时,这个修改只会影响子类本身,不会影响父类或其他子类。
- 修改父类的类变量会影响那些没有修改过该类变量的子类。
class Parent:
class_variable = "Parent variable"
class Child1(Parent):
pass
class Child2(Parent):
pass
print(Parent.class_variable) # 输出: Parent variable
print(Child1.class_variable) # 输出: Parent variable
print(Child2.class_variable) # 输出: Parent variable
# 修改 Child1 的类变量
Child1.class_variable = "Child1 variable"
print(Parent.class_variable) # 输出: Parent variable
print(Child1.class_variable) # 输出: Child1 variable
print(Child2.class_variable) # 输出: Parent variable
# 修改 Parent 的类变量
Parent.class_variable = "Modified Parent variable"
print(Parent.class_variable) # 输出: Modified Parent variable
print(Child1.class_variable) # 输出: Child1 variable
print(Child2.class_variable) # 输出: Modified Parent variable
5. __init__和__new__方法有什么区别?
5.1 __new__
方法
- 作用:
__new__
是一个静态方法,用于创建并返回一个新的实例。它在对象实例化的过程中第一个被调用。 - 参数:
__new__
方法至少接受一个参数cls
,表示要实例化的类。 - 返回值:必须返回一个类的实例(通常是通过
super().__new__(cls)
调用来创建实例)。 - 用途:通常在继承自不可变类型(如
int
、str
、tuple
)的类中使用,或在需要控制实例创建过程时使用。
5.2 __init__
方法
- 作用:
__init__
是一个实例方法,用于初始化实例的属性。它在对象创建后被调用,通常用于设置对象的初始状态。 - 参数:
__init__
方法至少接受一个参数self
,表示要初始化的实例对象。 - 返回值:不返回值(返回
None
)。 - 用途:用于为新创建的对象设置初始状态或属性值。
5.3 调用顺序
__new__
被调用以创建一个新实例。- 新实例作为参数传递给
__init__
,并进行初始化。
5.4 总结
__new__
用于控制对象的创建过程,是一个类方法,返回一个新的实例。__init__
用于初始化对象的属性,是一个实例方法,不返回值。__new__
在__init__
之前被调用。- 对于不可变类型的子类,通常需要重写
__new__
方法,而对于一般的类,只需要重写__init__
方法来初始化对象。
6. 闭包
闭包必须满足以下三个条件:
- 必须有一个内嵌函数。
- 内嵌函数引用了外部函数中的变量。
- 外部函数返回内嵌函数。