1.深拷贝与浅拷贝
- 浅拷贝是对地址的拷贝,只拷贝第一层,第一层改变的时候不会改变,内层改变才会改变。
- 深拷贝是对值的拷贝,不会随着原数据的变化而变化
import copy
list = [[1, 2], 'feng', 66]
a = copy.copy(list)
b = copy.deepcopy(list)
# list[1]=4
# print(a)
# print(b)
#输出结果
# [[1, 2], 'feng', 66]
# [[1, 2], 'feng', 66]
list[0][0] = 8
print(a)
print(b)
#输出结果
# [[8, 2], 'feng', 66]
# [[1, 2], 'feng', 66]
import copy
list = ['feng',[1,2], 66]
a = copy.copy(list)
b = copy.deepcopy(list)
print(id(list[0]))
print(id(a[0]))
print(id(b[0]))
print(id(list[1]))
print(id(a[1]))
print(id(b[1]))
#输出结果
# 4306918640
# 4306918640
# 4306918640
# 4307415552
# 4307415552
# 4307500992
2.迭代器
迭代器是一个可以记住遍历位置的对象,因此不会像列表那样一次性全部生成,而是可以等到用的时候才生成,因此节省了大量的内存资源。迭代器对象从集合中的第一个元素开始访问,直到所有的元素被访问完。迭代器有两个方法:iter()
和next()
方法。
迭代器的优点: 省内存.它是一种通过延时创建的方式生成一个序列,只有在需要的时候才被创建。
__iter__
和__next__
执行流程:
- 先调用
__iter()__
,得到可迭代对象的迭代器- 调用
__next()__
,将上一步得到的迭代器 进行取值- 将上一步取出来的值赋值给变量
- 重复执行,所有数据都获取完毕后,会在下一次调用
__next__
的时候产生Stopiteration
异常。只不过for
循环中自带了异常处理,当它遇到Stopiteration
异常的时候,会自动结束for
循环
示例代码:
# 自定义一个迭代器,求斐波那契序列
from itertools import islice
from collections import Iterator,Iterable
class Fib(object):
def __init__(self):
self.prev = 0
self.curr = 1
def __iter__(self):
return self
def __next__(self):
value = self.curr
self.curr += self.prev
self.prev = value
return value
# 迭代器对象
f = Fib()
print(isinstance(f,Iterator)) # true
L = list(islice(f,0,10)) # islice对可迭代对象进行切片
print(L)
# [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
3.生成器
在Python中,一边循环一边计算的机制,称为生成器:generator。使用了 yield 的函数被称为生成器(generator)。
生成器仅仅保存了一套生成数值的算法,并且没有让这个算法现在就开始执行,而是什么时候调它,它什么时候开始计算一个新的值,并返回。生成器是一种特殊的迭代器,能够在遍历时暂停和继续执行,使用yield定义的函数不会执行,返回一个生成器对象。迭代时遇到yield会暂停并返回yield后面的值,保存当前的状态,下次迭代从上次暂停的地方继续执行,直到再次遇到yield,优势是内存占用少,节约资源。应用场景是遍历文件或网络数据流、CPU密集型计算、图像处理等。生成器可以逐个读取大文件,并且不必将整个文件加载到内存中,避免了内存消耗和IO操作的额外开销。
当我们创建一个生成器时,第一次调用只能用
next()
或者send(None)
来启动生成器实际上next()
和send()
在一定意义上作用是相似的,区别是send()
可以传递yield
表达式的值进去,而next()
不能传递特定的值,只能传递None
进去。因此,我们可以看做c.next()
和c.send(None)
作用是一样的。next
和send
都是调用yield
生成值的函数,next
是直接调用,send
是先覆盖上一个yield
返回值后再调用下一个yield
生成值。
4.装饰器
装饰器是在函数原有功能不变的基础上加上新的功能。底层采用闭包理念实现。内外函数嵌套,外层函数返回内层函数,内层函数引用外部函数的变量。
装饰器分为有参装饰器和无参装饰器。当func被多个装饰器修饰时,装饰器会按照从下到上的顺序对func进行装饰,也就是最靠近函数的装饰器最先应用装饰,执行的时候从上往下执行。
应用场景:权限验证,日志记录,接口执行时间统计,接口过滤
- 权限验证:比如在后台管理系统中,在装饰器中检查用户权限,按照权限判断用户是否可以访问此接口;
- 日志记录:在函数执行前后添加日志记录,用于监控函数的调用情况
代码示例:
# 多个装饰器装饰一个原函数
# 装饰器1:在调用原函数前输出 "装饰器1的开始";在调用原函数结束输出 "装饰器1的结束"
def outer1(f):
def inner():
print("装饰器1的开始")
f()
print("装饰器1的结束")
return inner
# 装饰器2:在调用原函数前输出 "装饰器2的开始";在调用原函数结束输出 "装饰器2的结束"
def outer2(f):
def inner():
print("装饰器2的开始")
f()
print("装饰器2的结束")
return inner
# 原函数 输出“我是原函数”
@outer2
@outer1
def func():
print("我是原函数")
func()
# 总结:
# 装饰器的调用顺序:从下往上
# 装饰器的执行顺序:从上往下 -->执行结果
5.进程、线程、协程
进程是操作系统进行分配资源的基本单位,每个进程都有自己的内存空间和系统资源,进程之间不能共享内存,需要通过IPC(进程间通信:管道、队列、数据库)的方式进行通信。适合CPU密集型场景。
比如:并行计算、网络编程、爬虫、任务调度(定时任务、异步任务)、分布式计算。
线程是程序执行的最小单元,一个进程可以有多个线程,同一个进程中的多个线程共享该进程中的全部资源,进程和线程都有五种状态:初始态、就绪态、执行态、阻塞态、终止态。适合IO密集型场景。
比如:多线程爬虫,多线程处理订单
协程(Coroutine,又称微线程)是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制。
协程可以比作子程序,但执行过程中,子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。协程之间的切换不需要涉及任何系统调用或任何阻塞调用。
协程只在一个线程中执行,是子程序之间的切换,发生在用户态上。而且,线程的阻塞状态是由操作系统内核来完成,发生在内核态上,因此协程相比线程节省线程创建和切换的开销。
6.高阶函数
高阶函数,它指的是一个函数可以接收另一个函数作为参数,或者将函数作为结果返回。
1.map(func, list):
将func 应用于list中的每一个元素,返回一个迭代器
2.reduce(func, list):
计算结果与下一个数据做累积计算,必须有两个参数
from functools import reduce
3.filter(func, list):
过滤掉不符合条件的元素,返回一个filter对象,可用list()转换
4.sorted(iterable[,cmp][,key][,reverse])
从iterable的项目中返回一个新的排序后的列表。可选的参数和列表方法与sort中的相同
list_sort = sorted(d,key=lambda x:x['a'],reverse=True)
7.魔法方法
Python 的魔法方法(Magic Methods),也称为特殊方法或双下划线方法,用于定义类的行为和特性。这些方法在特定的情况下会被自动调用。
魔法方法的分类:
初始化与析构:如
__init__
、__new__
、__del__
等,控制对象的创建和销毁。
属性访问:如__getattr__
、__getattribute__
、__setattr__
、__delattr__
等,控制属性的访问和修改。
容器方法:如__len__
、__getitem__
、__setitem__
、__delitem__
等,支持容器类型的操作。
数值方法:如__add__
、__sub__
、__mul__
等,支持数学运算。
字符串与序列表示:如__str__
、__repr__
、__format__
等,提供对象的字符串表示。
上下文管理:如__enter__
、__exit__
等,支持with
语句的上下文管理。
比较操作符:如__eq__
、__ne__
、__lt__
、__le__
、__gt__
、__ge__
等,支持对象比较。
其他:如__call__
、__hash__
等,提供额外的功能
以下是一些常用的 Python 魔法方法详解:
__new__
: 用于创建对象实例时调用。它在对象实例化之前被调用,并且必须返回一个新的对象实例,在使用普通的类实例化对象时,默认会先调用__new__
方法来创建对象实例,然后再调用__init__
方法进行初始化。但是,我们可以重写__new__
方法以自定义对象的创建过程。__init__(self, [...])
: 构造函数,在创建对象时调用,用于初始化对象的属性。__del__(self)
: 析构函数,在对象被销毁时调用,用于清理资源。__str__(self)
: 字符串表示方法,在使用str()
函数或print()
函数打印对象时调用。__repr__(self)
: 表示方法,返回对象的完整、可读性较好的字符串表示形式,在交互模式下会自动调用。__len__(self)
: 长度方法,在使用len()
函数获取对象长度时调用。__getitem__(self, key)
: 索引访问方法,在通过索引访问对象时调用。__setitem__(self, key, value)
: 索引赋值方法,在通过索引设置对象值时调用。__iter__(self)
: 迭代器方法,返回一个对象的迭代器,在使用 for 循环遍历对象时调用。__next__(self)
: 迭代器中的下一个元素方法,在迭代器中调用以获取下一个元素。__eq__(self, other)
: 相等比较方法,判断对象是否相等时调用。__lt__(self, other)
: 小于比较方法,判断对象是否小于另一个对象时调用。__gt__(self, other)
: 大于比较方法,判断对象是否大于另一个对象时调用。此外,还有很多其他的魔法方法可以用于自定义类的行为,比如数学运算相关的
__add__
、__sub__
、__mul__
、__div__
、__pow__
等。详细的魔法方法列表可参考 Python 官方文档。通过定义这些方法,我们可以灵活地控制和定制类的操作和行为。
与python反射相关的魔法方法:
反射使得程序能够在运行时动态地访问和操作对象的内部结构,包括属性和方法。
__getattr__(self, name)
:当访问的属性不存在时调用。适用于动态属性访问。
__getattribute__(self, name)
:访问任何属性时调用,拦截所有对对象属性的访问。谨慎重写,因为它可能导致无限递归。
__setattr__(self, name, value)
:用于设置或更新对象的属性。当你为对象的属性赋值时,Python会自动调用这个方法。
__delattr__(self, name)
:删除属性时调用。当使用del
语句或delattr
函数删除对象的属性时,如果类中定义了__delattr__
方法,Python会调用这个方法而不是直接删除属性。
__dir__(self)
:获取对象的所有属性名和方法名列表,用于dir()
函数
8.python垃圾回收机制
在Python中,垃圾回收(Garbage Collection)是一种自动化的内存管理机制,它通过检测和清除不再使用的内存来释放内存资源,从而提高程序的性能和效率。
Python使用了一种称为"引用计数"的策略来跟踪对象的引用次数。每当一个对象被引用时,其引用计数就会增加;当它不再被引用时,引用计数就会减少。 当一个对象的引用计数达到零时,该对象就成为垃圾对象,将被垃圾回收机制清理掉。
除了引用计数,Python还使用了其他垃圾回收策略来处理一些特殊情况,会在下面介绍。
-
标记-清除(Mark and Sweep): 当引用计数策略无法解决循环引用问题时,Python使用标记-清除算法来检测和清除不再使用的对象。该算法的原理是,首先从根对象(如全局变量、函数调用栈等)开始标记所有可访问的对象,然后清除那些未标记的对象。
-
分代回收(Generational Collection): Python中的对象按照其年龄进行分组,被分为三代:0代、1代和2代。大部分新创建的对象属于0代,当一个对象经过一次垃圾回收后仍然存活,它会被移到下一代。分代回收机制的原理是,根据经验观察到的现象,大部分对象在短时间内就会变成垃圾,而那些存活得更久的对象更可能长时间存活下去。因此,分代回收机制可以根据对象的存活情况进行不同频率的垃圾回收,提高效率。
-
内存池机制(Memory Pools): Python使用内存池来管理小块内存的分配和释放。内存池是一块连续的内存空间,包含多个大小固定的块,每个块都可以独立分配给一个对象。这个机制显著减少了内存碎片的产生,并且提高了内存分配的效率。
需要注意的是,Python的垃圾回收是自动进行的,开发者不需要显式地调用垃圾回收函数。垃圾回收机制是Python解释器的一部分,它会在适当的时候自动运行,确保内存资源的有效利用。