- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章python编码最佳实践之总结由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
相信用python的同学不少,本人也一直对python情有独钟,毫无疑问python作为一门解释性动态语言没有那些编译型语言高效,但是python简洁、易读以及可扩展性等特性使得它大受青睐.
工作中很多同事都在用python,但往往很少有人关注它的性能和惯用法,一般都是现学现用,毕竟python不是我们的主要语言,我们一般只是使用它来做一些系统管理的工作。但是我们为什么不做的更好呢?python zen中有这样一句:There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. 大意就是python鼓励使用一种最优的方法去完成一件事,这也是和ruby等的一个差异。所以一种好的python编写习惯个人认为很重要,本文就重点从性能角度出发对python的一些惯用法做一个简单总结,希望对大家有用~ 。
提到性能,最容易想到的是降低复杂度,一般可以通过测量代码回路复杂度(cyclomatic complexitly)和Landau符号(大O)来分析, 比如dict查找是O(1),而列表的查找却是O(n),显然数据的存储方式选择会直接影响算法的复杂度.
1、数据结构的选择 1. 在列表中查找:
对于已经排序的列表考虑用bisect模块来实现查找元素,该模块将使用二分查找实现 。
1
2
3
4
5
|
def
find(seq, el) :
pos
=
bisect(seq, el)
if
pos
=
=
0
or
( pos
=
=
len
(seq)
and
seq[
-
1
] !
=
el ) :
return
-
1
return
pos
-
1
|
而快速插入一个元素可以用
1
|
bisect.insort(
list
, element)
|
这样就插入元素并且不需要再次调用 sort() 来保序,要知道对于长list代价很高. 。
2. set代替列表:
比如要对一个list进行去重,最容易想到的实现:
1
2
3
4
5
|
seq
=
[
'a'
,
'a'
,
'b'
]
res
=
[]
for
i
in
seq:
if
i
not
in
res:
res.append(i)
|
显然上面的实现的复杂度是O(n2),若改成:
1
2
|
seq
=
[
'a'
,
'a'
,
'b'
]
res
=
set
(seq)
|
复杂度马上降为O(n),当然这里假定set可以满足后续使用.
另外,set的union,intersection,difference等操作要比列表的迭代快的多,因此如果涉及到求列表交集,并集或者差集等问题可以转换为set来进行,平时使用的时候多注意下,特别当列表比较大的时候,性能的影响就更大.
3. 使用python的collections模块替代内建容器类型:
collections有三种类型:
deque:增强功能的类似list类型 defaultdict:类似dict类型 namedtuple:类似tuple类型 。
列表是基于数组实现的,而deque是基于双链表的,所以后者在中间or前面插入元素,或者删除元素都会快很多.
defaultdict为新的键值添加了一个默认的工厂,可以避免编写一个额外的测试来初始化映射条目,比dict.setdefault更高效,引用python文档的一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#使用profile stats工具进行性能分析
>>>
from
pbp.scripts.profiler
import
profile, stats
>>> s
=
[(
'yellow'
,
1
), (
'blue'
,
2
), (
'yellow'
,
3
),
... (
'blue'
,
4
), (
'red'
,
1
)]
>>> @profile(
'defaultdict'
)
...
def
faster():
... d
=
defaultdict(
list
)
...
for
k, v
in
s:
... d[k].append(v)
...
>>> @profile(
'dict'
)
...
def
slower():
... d
=
{}
...
for
k, v
in
s:
... d.setdefault(k, []).append(v)
...
>>> slower(); faster()
Optimization: Solutions
[
306
]
>>> stats[
'dict'
]
{
'stones'
:
16.587882671716077
,
'memory'
:
396
,
'time'
:
0.35166311264038086
}
>>> stats[
'defaultdict'
]
{
'stones'
:
6.5733464259021686
,
'memory'
:
552
,
'time'
:
0.13935494422912598
}
|
可见性能提升了快3倍。defaultdict用一个list工厂作为参数,同样可用于内建类型,比如long等.
除了实现的算法、架构之外,python提倡简单、优雅。所以正确的语法实践又很有必要,这样才会写出优雅易于阅读的代码.
2、语法最佳实践 字符串操作:优于python字符串对象是不可改变的,因此对任何字符串的操作如拼接,修改等都将产生一个新的字符串对象,而不是基于原字符串,因此这种持续的 copy会在一定程度上影响Python的性能: (1)用join代替 '+' 操作符,后者有copy开销; 。
(2)同时当对字符串可以使用正则表达式或者内置函数来处理的时候,选择内置函数。如str.isalpha(),str.isdigit(),str.startswith((‘x', ‘yz')),str.endswith((‘x', ‘yz')) 。
(3)字符格式化操作优于直接串联读取:
str = "%s%s%s%s" % (a, b, c, d) # efficient str = "" + a + b + c + d + "" # slow 。
2. 善用list comprehension(列表解析) & generator(生成器) & decorators(装饰器),熟悉itertools等模块:
(1) 列表解析,我觉得是python2中最让我印象深刻的特性,举例1:
1
2
3
4
5
6
7
8
9
10
11
12
|
>>>
# the following is not so Pythonic
>>> numbers
=
range
(
10
)
>>> i
=
0
>>> evens
=
[]
>>>
while
i <
len
(numbers):
>>>
if
i
%
2
=
=
0
: evens.append(i)
>>> i
+
=
1
>>> [
0
,
2
,
4
,
6
,
8
]
>>>
# the good way to iterate a range, elegant and efficient
>>> evens
=
[ i
for
i
in
range
(
10
)
if
i
%
2
=
=
0
]
>>> [
0
,
2
,
4
,
6
,
8
]
|
。
举例2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
def
_treament(pos, element):
return
'%d: %s'
%
(pos, element)
f
=
open
(
'test.txt'
,
'r'
)
if
__name__
=
=
'__main__'
:
#list comps 1
print
sum
(
len
(word)
for
line
in
f
for
word
in
line.split())
#list comps 2
print
[(x
+
1
, y
+
1
)
for
x
in
range
(
3
)
for
y
in
range
(
4
)]
#func
print
filter
(
lambda
x: x
%
2
=
=
0
,
range
(
10
))
#list comps3
print
[i
for
i
in
range
(
10
)
if
i
%
2
=
=
0
]
#list comps4 pythonic
print
[_treament(i, el)
for
i, el
in
enumerate
(
range
(
10
))]
output:
24
[(
1
,
1
), (
1
,
2
), (
1
,
3
), (
1
,
4
), (
2
,
1
), (
2
,
2
), (
2
,
3
), (
2
,
4
), (
3
,
1
), (
3
,
2
), (
3
,
3
), (
3
,
4
)]
[
0
,
2
,
4
,
6
,
8
]
[
0
,
2
,
4
,
6
,
8
]
[
'0: 0'
,
'1: 1'
,
'2: 2'
,
'3: 3'
,
'4: 4'
,
'5: 5'
,
'6: 6'
,
'7: 7'
,
'8: 8'
,
'9: 9'
]
|
没错,就是这么优雅简单.
(2) 生成器表达式在python2.2引入,它使用'lazy evaluation'思想,因此在使用内存上更有效。引用python核心编程中计算文件中最长的行的例子:
1
2
3
4
|
f
=
open
(
'/etc/motd, '
r')
longest
=
max
(
len
(x.strip())
for
x
in
f)
f.close()
return
longest
|
这种实现简洁而且不需要把文件文件所有行读入内存.
(3) python在2.4引入装饰器,又是一个让人兴奋的特性,简单来说它使得函数和方法封装(接收一个函数并返回增强版本的函数)更容易阅读、理解。'@'符号是装饰器语法,你可以装饰一个函数,记住调用结果供后续使用,这种技术被称为memoization的,下面是用装饰器完成一个cache功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
import
time
import
hashlib
import
pickle
from
itertools
import
chain
cache
=
{}
def
is_obsolete(entry, duration):
return
time.time()
-
entry[
'time'
] > duration
def
compute_key(function, args, kw):
#序列化/反序列化一个对象,这里是用pickle模块对函数和参数对象进行序列化为一个hash值
key
=
pickle.dumps((function.func_name, args, kw))
#hashlib是一个提供MD5和sh1的一个库,该结果保存在一个全局字典中
return
hashlib.sha1(key).hexdigest()
def
memoize(duration
=
10
):
def
_memoize(function):
def
__memoize(
*
args,
*
*
kw):
key
=
compute_key(function, args, kw)
# do we have it already
if
(key
in
cache
and
not
is_obsolete(cache[key], duration)):
print
'we got a winner'
return
cache[key][
'value'
]
# computing
result
=
function(
*
args,
*
*
kw)
# storing the result
cache[key]
=
{
'value'
: result,
-
'time'
: time.time()}
return
result
return
__memoize
return
_memoize
@memoize
()
def
very_very_complex_stuff(a, b, c):
return
a
+
b
+
c
print
very_very_complex_stuff(
2
,
2
,
2
)
print
very_very_complex_stuff(
2
,
2
,
2
)
@memoize
(
1
)
def
very_very_complex_stuff(a, b):
return
a
+
b
print
very_very_complex_stuff(
2
,
2
)
time.sleep(
2
)
print
very_very_complex_stuff(
2
,
2
)
|
运行结果:
1
2
3
4
5
6
7
8
9
|
6
we got a winner
6
4
4
|
。
装饰器在很多场景用到,比如参数检查、锁同步、单元测试框架等,有兴趣的人可以自己进一步学习.
3. 善用python强大的自省能力(属性和描述符):自从使用了python,真的是惊讶原来自省可以做的这么强大简单,关于这个话题,限于内容比较多,这里就不赘述,后续有时间单独做一个总结,学习python必须对其自省好好理解.
3、 编码小技巧 1、在python3之前版本使用xrange代替range,因为range()直接返回完整的元素列表而xrange()在序列中每次调用只产生一个整数元素,开销小。(在python3中xrange不再存在,里面range提供一个可以 遍历任意长度的范围的iterator) 2、if done is not None比语句if done != None更快; 3、尽量使用"in"操作符,简洁而快速: for i in seq: print i 4、'x < y < z'代替'x < y and y < z'; 5、while 1要比while True更快, 因为前者是单步运算,后者还需要计算; 6、尽量使用build-in的函数,因为这些函数往往很高效,比如add(a,b)要优于a+b; 7、在耗时较多的循环中,可以把函数的调用改为内联的方式,内循环应该保持简洁。 8、使用多重赋值来swap元素:
x, y = y, x # elegant and efficient 。
而不是:
temp = x x = y y = temp 。
9. 三元操作符(python2.5后):V1 if X else V2,避免使用(X and V1) or V2,因为后者当V1=""时,就会有问题.
10. python之switch case实现:因为switch case语法完全可用if else代替,所以python就没 有switch case语法,但是我们可以用dictionary或lamda实现:
switch case结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
switch
(var)
{
case
v1: func1();
case
v2: func2();
...
case
vN: funcN();
default
: default_func();
}
dictionary实现:
values = {
v1: func1,
v2: func2,
...
vN: funcN,
}
values.get(var, default_func)()
lambda实现:
{
'1'
: lambda: func1,
'2'
: lambda: func2,
'3'
: lambda: func3
}[value]()
|
用try…catch来实现带Default的情况,个人推荐使用dict的实现方法.
这里只总结了一部分python的实践方法,希望这些建议可以帮助到每一位使用python的同学,优化性能不是重点,高效解决问题,让自己写的代码更加易于维护! 。
最后此篇关于python编码最佳实践之总结的文章就讲到这里了,如果你想了解更多关于python编码最佳实践之总结的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
本文分享自华为云社区《大模型LLM之分布式训练》,作者: 码上开花_Lancer。 随着语言模型参数量和所需训练数据量的急速增长,单个机器上有限的资源已无法满足大语言模型训练的要求。需要设计分布式训
本文分享自华为云社区《五大基础算法--动态规划法》,作者: 大金(内蒙的)。 一、基本概念 动态规划法,和分治法极其相似。区别就是,在求解子问题时,会保存该子问题的解,后面的子问题求解时,可以直接拿来
pip install scp pip install pexpect 测试代码: import os import stat import paramiko # 用于调用scp命令 def s
我目前正在实现“ token ”REST 服务。 token 只是一个字符串,由一些参数构建而成,然后经过哈希处理并在一定时间后过期。 我想在我的 REST 服务中有一个可以验证 token 的端点,
打开软删除后,我在客户端上添加一条记录,推送,删除添加的记录推送,然后尝试使用与初始记录相同的主键添加新记录(然后推送),我得到一个异常(exception)。 EntityDomainManager
打开软删除后,我在客户端上添加一条记录,推送,删除添加的记录推送,然后尝试使用与初始记录相同的主键添加新记录(然后推送),我得到一个异常(exception)。 EntityDomainManager
我有一个应用程序,每 x 秒接收一次天气信息。我想将此数据保存到 XML 文件中。 我应该为每个天气通知创建一个新的 XML 文件,还是将每个通知附加到同一个 XML 文件中?我不确定 XML 标准的
我猜我们大多数人都必须在某个时候处理这个问题,所以我想我会问这个问题。 当您的 BLL 中有很多集合并且您发现自己一遍又一遍地编写相同的旧内联(匿名)谓词时,显然有必要进行封装,但实现封装的最佳方
我有一些 c# 代码已经运行了一段时间了..我不得不说,虽然我了解 OO 原则的基础知识,但显然有不止一种方法可以给猫剥皮(尽管我讨厌那个短语!)。 因此,我有一个基本抽象类作为基本数据服务类,如下所
我设计了一个 SQL 数据库系统(使用 Postgre),我有一个问题,即创建一个关系/引用的常见做法是什么,这种关系/引用即使在引用的对象被删除时也能持续存在。 比如有一个UserORM,还有Act
我们的目标是搜索用户输入的字符串并计算在其中找到多少元音。不幸的是我被困在这里,有什么帮助吗? def numVowels(s): vowels= "AEIOUaeiou" if s
我有一个适用于我的“items”int 数组的旋转函数。下面的代码完成了它,除了我不必要地传输值。我正在努力实现“就地”轮换。我的意思是 ptrs 会递增或递减,而不是从数组中获取值。我需要通过这种方
我有一个 json 存储在我的应用程序文档文件夹中,我需要在我的所有 View 中使用它。我正在加载 json 并将其添加到每个 View 中的 NSMutableArray。但现在我了解到,我可以将
我用 C++ 开始了一个项目。这种语言的内存管理对我来说是新的。 我过去常常使用 new () 创建对象,然后传递指针,虽然它可以工作,但调试起来很痛苦,人们看到代码时会用有趣的眼神看着我。我为它没有
已结束。 这个问题是 off-topic .它目前不接受答案。 想要改进这个问题? Update the question所以它是on-topic堆栈溢出。 关闭 10 年前。 Improve thi
保持类松散耦合是编写易于理解、修改和调试的代码的一个重要方面——我明白这一点。然而,作为一个新手,几乎任何时候我都会超越我所苦苦挣扎的最简单的例子。 我或多或少地了解如何将字符串、整数和简单数据类型封
我发现我需要编写大量重复代码,因为我无法从其他 Controller 调用函数。例如,这里新闻提要内容在我的代码中重复,我对一个 Controller 做一些特定的事情,然后需要像这样加载我的新闻提要
假设需要一种数字数据类型,其允许值在指定范围内。更具体地说,假设要定义一个整数类型,其最小值为0,最大值为5000。这种情况在很多情况下都会出现,例如在对数据库数据类型,XSD数据类型进行建模时。 在
假设我想循环整个数组来访问每个元素。使用 for 循环、for...in 循环或 for...of 循环是 JavaScript 开发人员的标准做法吗? 例如: var myArray = ["app
我有一个旧的 SL4/ria 应用程序,我希望用 Breeze 取代它。我有一个关于内存使用和缓存的问题。我的应用程序加载工作列表(一个典型的用户可以访问大约 1,000 个这些工作)。此外,还有很多
我是一名优秀的程序员,十分优秀!