Python 程序优化之加速运行的技巧

优化三个原则:

  • 1.不要过早的优化,先要让程序实现功能,然后在进行优化
  • 2.权衡优化代价,优化是有代价的,通常面临的是时间与空间的交换,以及开发代价的也需要考虑
  • 3.不要优化那些无关紧要的部分,专注运行慢的地方优化即可(如内部循环)

加速技巧:

1.避免全局变量

定义在全局范围内的代码运行速度会比定义在函数中的慢很多 ,将全局范围内定义的代码放到函数中速度将带来15%-30%的提升.

2.避免模块和函数属性访问

一:

imort math

  代码中用math.sqrt()

二:

form math import sqrt

  代码中用sqrt()

第一种的话,每次使用’. ’,(属性访问操作符),都会触发特定的操作,如__getattribute__()和__getattr__(),这些操作会进行字典操作,带来额外的时间开销.通过方法二,from 即可消除属性访问操作带来的问题.

三:

import math

def xx():

  sqrt = math.sqrt

同时可以进一步优化,可以看出方法二中的sqrt(),是全局变量,因此可以将将其放入函数中变为局部变量,面对频繁访问sqrt的代码段这样的操作可以提速很多,这里值得注意的是,这有个常见的操作就是:

for i in range(n):

  list.append (I)

这里也频繁的调用属性访问操作,因此可以将其赋值给局部变量

append = list.append

for i in range(n):

  append(i)

3.避免类内属性的访问

同样出现了类内属性的频繁访问的话,比如循环中出现self.value,那么最好将他赋值给局部变量,这样访问速度会快一点.

4.避免不必要的抽象

使用额外的处理层(如装饰器,属性访问,描述器)等去包装代码时,会让代码变得很慢,如

@ property

@ setter

这些都是C++遗留下的代码风格,如没有必要的话就将其简化

4.避免数据无意义的复制

以及避免滥用copy.deepcopy()

5.交换值时避免使用中间变量

如:

temp = a

a = b

b = temp

改进为:

a, b = b, a

6.避免用+进行字符串的拼接,用join()

a+b,由于Python中字符串是不可变对象,其会申请一块内存空间,将a, b分别复制到新申请的内存空间中,因此面对频繁的字符串拼接,如n个字符串的拼接,会产生n-1个中间结果,每个中间结果都需要申请和复制一次内存,严重影响运行效率.而在用join()进行字符串的拼接时,会首先计算出需要的申请的总的内存空间,然后一次性地申请所需内存,并将每个字符串元素复制到该内存中去.

7.利用if条件的短路特性

if a and b: #如果a为true,则不再计算b

if a or b : #如果 a为 Flase 则不再计算 b

因此编程时要预判a b两个条件的为真的概率,根据概率决定谁在and (or) 前,减少没必要的计算.

8.用for 循环代替while循环,for循环比while循环快不少.,同时用隐式for 循环代替显示for循环.

显示

def computeSum(size):

  sum = 0

  for I in range(size) :

    sum += 1

  return sum

 

隐式 :

def computeSum(size):

  return sum(range(size))

少去了一些中间变量的创建等操作.

9.减少内层for循环的计算

如:

import math

def computeSqrt (m, n):

  sqrt = math.sqrt

  for x in range (m):

    for y in range(n):

      z = sqrt(x) + sqrt(y)

看起来合乎逻辑,其实sqrt(x)产生了很多不必要的计算

改进:

import math

def computeSqrt (m, n):

  sqrt = math.sqrt

  for x in range (m):

    x_sqrt = sqrt (x)

    for y in range(n):

      z = x_sqrt + sqrt(y)

10.使用numba.jit进行加速

numba是一个用于编译Python数组和数值计算函数的编译器,这个编译器能够大幅提高直接使用Python编写的函数的运算速度。

Numba的主要特性:

  • 动态代码生成 (在用户偏爱的导入期和运行期)

  • 为CPU(默认)和GPU硬件生成原生的代码

  • 集成Python的科学软件栈(Numpy)

只需要在函数前加上@numba.jit装饰器即可(未完待续)

11.选择合适的数据结构

Python中内置的数据结构,如str, tuple,list, set,dict底层都是c实现的,速度已经很快了.其中list,是一种动态数组,其会预分配一定的内存空间,当预分配的内存空间用完之后,又继续向其中添加元素时,会申请一个更大的内存空间,将原来的所有的元素都复制过去,之后销毁原来的内存空间,在插入新的元素.删除元素的操作类似,当已经使用的空间比原先分分配的空间一半还少时,会另外申请一个更小的空间,做一次元素的复制,之后销毁原来的大内存空间.因此如果有频繁的新增,删除操作时,且新增,删除的元素又比较多,这时的list效率就比较低,可以考虑使用,collections.deque.

class collections.deque([iterable[, maxlen]])

返回一个新的双向队列对象,从左到右初始化(用方法 append()) ,从 iterable (迭代对象) 数据创建。如果 iterable 没有指定,新队列为空。

Deque队列是由栈或者queue队列生成的(发音是 “deck”,”double-ended queue”的简称)。Deque 支持线程安全,内存高效添加(append)和弹出(pop),从两端都可以,两个方向的大概开销都是 O(1) 复杂度

虽然 list 对象也支持类似操作,不过这里优化了定长操作和 pop(0) 和 insert(0, v) 的开销。它们引起 O(n) 内存移动的操作,改变底层数据表达的大小和位置。

如果 maxlen 没有指定或者是 None ,deques 可以增长到任意长度。否则,deque就限定到指定最大长度。一旦限定长度的deque满了,当新项加入时,同样数量的项就从另一端弹出。限定长度deque提供类似Unix filter tail 的功能。它们同样可以用与追踪最近的交换和其他数据池活动。

双向队列(deque)对象支持以下方法:

append(x)  添加 x 到右端。

appendleft(x)  添加 x 到左端。

clear()  移除所有元素,使其长度为0.

copy()  创建一份浅拷贝。

count(x)  计算 deque 中元素等于 x 的个数。

extend(iterable)   扩展deque的右侧,通过添加iterable参数中的元素。

extendleft(iterable)  扩展deque的左侧,通过添加iterable参数中的元素。注意,左添加时,在结果中iterable参数中的顺序将被反过来添加。

index(x[, start[, stop]])  返回 x 在 deque 中的位置(在索引 start 之后,索引 stop 之前)。 返回第一个匹配项,如果未找到则引发 ValueError。

insert(i, x)  在位置 i 插入 x 。如果插入会导致一个限长 deque 超出长度 maxlen 的话,就引发一个 IndexError。

pop()  移去并且返回一个元素,deque 最右侧的那一个。 如果没有元素的话,就引发一个 IndexError。

popleft()  移去并且返回一个元素,deque 最左侧的那一个。 如果没有元素的话,就引发 IndexError。

remove(value)  移除找到的第一个 value。 如果没有的话就引发 ValueError。

reverse()将deque逆序排列。返回 None 。

rotate(n=1)  向右循环移动 n 步。 如果 n 是负数,就向左循环。如果deque不是空的,向右循环移动一步就等价于  d.appendleft(d.pop()) , 向左循环一步就等价于 d.append(d.popleft()) 。

Deque对象同样提供了一个只读属性:

maxlen  Deque的最大尺寸,如果没有限定的话就是 None 。

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值