写在前面:
Python是一种解释型高级通用编程语言,具有广泛的用途,几乎可以将其用于所有事物。其以简单的语法、优雅的代码和丰富的第三方库而闻名。python除了有很多优点外,但在速度上还有一个非常大的缺点。
想要深入了解python,必须对python底层内容和结构有详细的了解。我们接下来要说明python内存、运行速度等。
问题1:python如何管理内存?
python有专用的空间管理机制,包括以下三方面。
一、对象池
1.1 小整数池
Python 对小整数的定义是 [-5, 256] ,这些整数对象是提前建立好的,不会被垃圾回收。在一个 Python 的程序中,无论这个整数处于LEGB(局部变量,闭包,全局,内建模块)中的哪个位置,所有位于这个范围内的整数使用的都是同一个对象。
1.2 大整数池
大整数的定义是大于256的数。大整数池:默认创建出来,池内为空的,创建一个就会往池中存储一个
1.3 inter机制(短字符串池)
每个单词(字符串),不夹杂空格或者其他符号,默认开启intern机制,共享内存,靠引用计数决定是否销毁。
二、垃圾回收
python采用的是引用计数机制为主,隔代回收和标记清除机制为辅的策略。
2.1 引用计数
当Python的某个对象的引用计数降为0时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾。比如某个新建对象,被分配给某个引用,对象的引用计数变为1。如 为0,那么该对象就可以被垃圾回收。
2.2标记清除
标记清除(Mark—Sweep)算法是一种基于追踪回收(tracing GC)技术实现的垃圾回收算法。
2.3 分代回收
因为,标记和清除的过程效率不高。清除非活动的对象前它必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象。还有一个问题就是:什么时候扫描去检测循环引用?为了解决上述的问题,python又引入了分代回收。分代回收解决了标记清楚时什么时候扫描的问题,并且将扫描的对象分成了3级,以及降低扫描的工作量,提高效率。
- 0代: 0代中对象个数达到700个,扫描一次。
- 1代: 0代扫描10次,则1代扫描1次。
- 2代: 1代扫描10次,则2代扫描1次。
三、怎么优化内存管理
1.手动垃圾回收
先调用del a,再调用gc.collect()即可手动启动GC。
2.调高垃圾回收阈值
gc.set_threshold 设置垃圾回收阈值(收集频率)。将 threshold 设为零会禁用回收。
3.避免循环引用
总结:python采用的是引用计数机制为主,标记-清除和分代回收(隔代回收)两种机制为辅的策略。
问题2:python如何加快代码运行速度,实现多线程?
提高Python的性能,有如下几方面要点:
- 第一是先编写简洁,高效的代码。
我们必须确保代码不会在循环中反复执行相同的计算。 - 第二不要为集合中的每个记录打开/关闭IO连接。
- 第三要确保在不需要时不创建新的对象实例。
具体方法:
- 选择合适的数据结构
使用正确的数据结构对python脚本的运行时间有显着影响。Python 有四种内置的数据结构:
列表、元组、集合、字典。
但是,大多数开发人员在所有情况下都使用列表。这是不正确的做法,应该根据任务使用合适数据结构。
例如,使用哈希表的数据结构。如果在程序中遇到大量搜索操作时,并且数据中没有重复项,则可以使用查找而不是循环。
一般来说,使用[]和{}来创建列表和字典相比使用list()和dict{}运行更加高效。这是因为使用list()和dict{}来创建对象时需要调用一个附加函数。
- 善用强大的内置函数和第三方库
Python有大量的库和内置函数来帮助你不用编写这些函数,几乎90%的问题已经有第三方包或内置函数来解决。Python中的许多内置函数都是用C实现的,并且经过了很好的优化,熟悉这些内置函数可以提高Python代码的性能。一些常用的内置函数有sum()、len()、map()、max()等。 - 少用循环
– 用 列表推导式代替循环
– 用 迭代器代替循环
– 用 filter() 代替循环
– 减少循环次数,精确控制,不浪费CPU
– 矢量化取代循环:尽量使用基于C构建的Python库,例如Numpy,Scipy和Pandas,并且利用矢量化同时处理来取代程序中编写多次处理数组单个元素的循环,循环可能是程序优化最容易被拿来开刀的地方了。例如:在对数组中每个元素求平方时直接用数组相乘,而不是两个for循环。
✅ 列表推导式
@timeshow
def f_list(n):
L = [i for i in range(n) if i % 7 == 0]
return L
✅ 迭代器
@timeshow
def f_iter(n):
L = (i for i in range(n) if i % 7 == 0)
return L
✅ 过滤器
@timeshow
def f_filter(n):
L = filter(lambda x: x % 7 == 0, range(n))
return L
- 避免循环重复计算
如果你有一个迭代器,必须用它的元素做一些耗时计算,比如匹配正则表达式。你应该将正则表达式模式定义在循环之外,因为最好只编译一次模式,而不是在循环的每次迭代中一次又一次地编译它。 - 少用内存、少用全局变量
内存占用是指程序运行时使用的内存量。为了让Python代码运行得更快,应该减少程序的内存使用量,即尽量减少变量或对象的数量。
Python 访问局部变量比全局变量更有效。在有必要之前,应该始终尝试忽略声明全局变量。一个在程序中定义过的全局变量会一直存在,直到整个程序编译完成,所以它一直占据着内存空间。另一方面,局部变量访问更快,且函数完成后即可回收。因此,使用多个局部变量比使用全局变量会更好。
# ❌ 应该避免的方式:
message = "Line1\n"
message += "Line2\n"
message += "Line3\n"
# ✅ 更好的方式:
l = ["Line1","Line2","Line3"]
message = '\n'.join(l)
# ❌ 应该避免的方式:
x = 5
y = 6
def add():
return x+y
add()
# ✅ 更好的方式:
def add():
x = 5
y = 6
return x+y
add()
- 精简代码行数
在编程时,尽量使用一些python的内置函数来精简代码行数,代码显得简洁凝练,大大提高代码运行效率。 - 使用多进程
一般计算机都是多进程的,那么在执行操作时可以使用Python中的multiproccessing。多进程可在代码中实现并行化。当您要实例化新进程,访问共享内存时,多进程成本很高,因此如果有大量数据处理时可以考虑使用多进程。然而,对于少量数据,则不提倡使用多进程。 - 使用Cpython
Cython是一个静态编译器,可以为您优化代码。加载cypthonmagic扩展并使用cython标记使用cython编译代码。
Cpython的安装:pip install Cython - 尽量使用csv替代xlsx
在进行数据处理时,需要更长的时间才能将数据加载到excel文件或从excel文件保存数据。相反,我们可以选择创建多个csv文件的路径,并创建了一个文件夹来对文件进行分组。 - 使用Dask来并行化Pandas DataFrame
Dask帮助我处理数据框中的数值函数和并行的numpy。