Python最差实践
创始人
2025-05-28 23:39:50
0

最近在看一些陈年老系统,其中有一些不好的代码习惯遗留下来的坑;加上最近自己也写了一段烂代码导致服务器负载飙升,所以就趁此机会总结下我看到过/写过的自认为不好的Python代码习惯,时刻提醒自己远离这些“最差实践”,避免挖坑。  

下面所举的例子中,有一部分会造成性能问题,有一部分会导致隐藏bug,或日后维护、重构困难,还有一部分纯粹是我认为不够pythonic。所以大家自行甄别,取精去糟吧。  

函数默认参数使用可变对象

这个例子我想大家应该在各种技术文章中见过许多遍了,也足以证明这是一个大坑。  

先看错误示范吧:

1

2

3

4

5

6

def use_mutable_default_param(idx=0, ids=[]):

    ids.append(idx)

    print(idx)

    print(ids)

use_mutable_default_param(idx=1)

use_mutable_default_param(idx=2)

输出:

1

2

3

4

1

[1]

2

[1, 2]

理解这其中的原因,最重要的是有两点:

函数本身也是一个对象,默认参数绑定于这个函数对象上

append这类方法会直接修改对象,所以下次调用此函数时,其绑定的默认参数已经不再是空list了

正确的做法如下:

1

2

3

4

5

6

def donot_use_mutable_default_param(idx=0, ids=None):

    if ids is None:

        ids = []

    ids.append(idx)

    print(idx)

    print(ids)

try…except不具体指明异常类型

虽然在Python中使用try…except不会带来严重的性能问题,但是不加区分,直接捕获所有类型异常的做法,往往会掩盖掉其他的bug,造成难以追查的bug。  

一般的,我觉得应该尽量少的使用try…except,这样可以在开发期尽早的发现问题。即使要使用try…except,也应该尽可能的指定出要捕获的具体异常,并在except语句中将异常信息记入log,或者处理完之后,再直接raise出来。  

关于dict的冗余代码

我经常能够看到这样的代码:

1

2

3

4

5

6

= {}

datas = [123423415]

for in datas:

    if not in d:

        d[k] = 0 

    d[k] += 1

其实,完全可以使用collections.defaultdict这一数据结构更简单优雅的实现这样的功能:

1

2

3

4

default_d = defaultdict(lambda0)

datas = [123423415]

for in datas:

    default_d[k] += 1

同样的,这样的代码:

1

2

3

4

# d is a dict

if 'list' not in d:

    d['list'= []

d['list'].append(x)

完全可以用这样一行代码替代:

1

2

# d is a dict

d.setdefault('list', []).append(x)

同样的,下面这两种写法一看就是带有浓浓的C味儿:

1

2

3

4

5

6

7

8

# d is a dict

for in d:

    = d[k]

    # do something

# l is a list

for in len(l):

    = l[i]

    # do something

应该用更pythonic的写法:

1

2

3

4

5

6

7

8

# d is a dict

for k, v in d.iteritems():

    # do something

    pass

# l is a list

for i, v in enumerate(l):

    # do something

    pass

另外,enumerate其实还有个第二参数,表示序号从几开始。如果想要序号从1开始数起,可以使用enumerate(l, 1)。  

使用flag变量而不使用for…else语句

同样,这样的代码也很常见:

1

2

3

4

5

6

7

8

9

10

11

search_list = ['Jone''Aric''Luise''Frank''Wey']

found = False

for in search_list:

    if s.startswith('C'):

        found = True

        # do something when found

        print('Found')

        break

if not found:

    # do something when not found

    print('Not found')

其实,用for…else更优雅:

1

2

3

4

5

6

7

8

9

search_list = ['Jone''Aric''Luise''Frank''Wey']

for in search_list:

    if s.startswith('C'):

        # do something when found

        print('Found')

        break

else:

    # do something when not found

    print('Not found')

过度使用tuple unpacking

在Python中,允许对tuple类型进行unpack操作,如下所示:

1

2

# human = ('James', 180, 32)

name,height,age = human

这个特性用起来很爽,比写name=human[0]之类的不知道高到哪里去了。所以,这一特性往往被滥用,一个human在程序的各处通过上面的方式unpack。  

然而如果后来需要在human中插入一个表示性别的数据sex,那么对于所有的这种unpack都需要进行修改,即使在有些逻辑中并不会使用到性别。

1

2

3

4

# human = ('James', 180, 32)

name,height,age, _ = human

# or

# name, height, age, sex = human

有如下几种方式解决这一问题:  

老老实实写name=human[0]这种代码,在需要使用性别信息处加上sex=human[3]

使用dict来表示human

使用namedtuple

1

2

3

# human = namedtuple('human', ['name', 'height', 'age', 'sex'])

= human('James'180320)

# then you can use h.name, h.sex and so on everywhere.

到处都是import *

import *是一种懒惰的行为,它不仅会污染当前的命名空间,并且还会使得pyflakes等代码检查工具失效。在后续查看代码或者debug的过程中,往往也很难从一堆import *中找到一个第三方函数的来源。  

可以说这种习惯是百害而无一利的。  

文件操作

文件操作不要使用裸奔的f = open(‘filename’)了,使用with open(‘filename’) as f来让context manager帮你处理异常情况下的关闭文件等乱七八糟的事情多好。  

野蛮使用class.name判断类型

我曾经遇见过一个bug:为了实现某特定功能,我新写了一个class B(A),在B中重写了A的若干函数。整个实现很简单,但是就是有一部分A的功能无法生效。最后追查到的原因,就是在一些逻辑代码中,硬性的判断了entity.__class__.__name__ == ‘A’。  

除非你就是想限定死继承层级中的当前类型(也就是,屏蔽未来可能会出现的子类),否则,不要使用__class__.__name__,而改用isinstance这个内建函数。毕竟,Python把这两个变量的名字都刻意带上那么多下划线,本来就是不太想让你用嘛。  

循环内部有多层函数调用

循环内部有多层函数调用,有如下两方面的隐患:  

Python没有inline函数,所以函数调用本来就会导致一定的开销,尤其是本身逻辑简单的时候,这个开销所占的比例就会挺可观的。

更严重的是,在之后维护这份代码时,会容易让人忽略掉函数是在循环中被调用的,所以容易在函数内部添加了一些开销较大却不必每次循环都调用的函数,比如time.localtime()。如果是直接一个平铺直叙的循环,我想大部分的程序员都应该知道把time.localtime()写到循环的外面,但是引入多层的函数调用之后,就不一定了哦。

所以我建议,在循环内部,如非特别复杂的逻辑,都应该直接写在循环里,不要进行函数调用。如果一定要包装一层函数调用,应该在函数的命名或注释中,提示后续的维护者,这个函数会在循环内部使用。  

Python是一门非常容易入门的语言,严格的缩进要求和丰富的内置数据类型,使得大部分Python代码都能做到比较好的规范。但是,不严格要求自己,也很容易就写出犯二的代码。上面列出的只是很小的一部分,唯有多读、多写、多想,才能培养敏锐的代码嗅觉,第一时间发现坏味道啊。  

相关内容

热门资讯

GDDS的主要内容 GDDS的主要内容国际货币基金组织(IMF)于1997年12月正式建立数据公布通用系统(GDDS),...
安全生产随感 安全生产随感摘要:每当打开电视机或摊开报纸,我们经常看到空难、海难、火灾、矿山事故、建筑事故等各种各...
项目管理研究生论文 项目管理研究生论文  导语:为保障工程建设项目融资的顺利进行,国家规定凡采用工程建设项目融资方式筹措...
从情死到情活-从X公司看民营... 从情死到情活-从X公司看民营企业文化再造企业文化已不再只是老板的文化,而是企业管理层的文化;中国民营...
会展南昌金秋宴 会展南昌金秋宴对南昌而言,一年一度的"金秋经贸活动月",无疑是一次会展经济的集体盛宴.一系列大型展会...
生态文明建设背景下民族体育精... 生态文明建设背景下民族体育精神研究论文  摘要:该文采用文献资料法、逻辑分析法,基于在中国民族传统体...
对背诵的再认识 对背诵的再认识本文主要从心理语言学理论和二语习得理论角度对背诵在语言学习中的价值进行论证.同时,文章...
动画专业论文提纲(2)   动画专业论文提纲(二)  摘要:在动画的成长进程中,技巧的主要无需置疑,不管是从幻灯到胶片、从无...
组织未来发展展望 组织未来发展展望未来是不确定的`.组织未来的目标将会增多,重点是满足其中若干项目目标达到最佳成果.未...
高中语文教师的核心素养论文 高中语文教师的核心素养论文  随着新课程标准的重新修订和国家从战略高度提出核心素养建设来,语文备受社...
全面推进依法治国论文 全面推进依法治国论文  一、依法治国与国家治理体系和治理能力现代化的关系  依法治国战略布局的提出,...
谈小学语文课堂教学的评价论文 谈小学语文课堂教学的评价论文  新课程背景下小学语文课堂强调要积极发挥评价的功能,注重将教师评价与学...
中国异针蟋属一新种记述(直翅... 中国异针蟋属一新种记述(直翅目,蟋蟀总科)记述蟋蟀总科、蛉蟋科、异针蟋属1新种:太白异针蟋Ptero...
进口商遭受信用证欺诈的主要形... 进口商遭受信用证欺诈的主要形式及防范措施论文  一、信用证的定义、特点及制度缺陷  (一)信用证的定...
泽楷,打造绿色未来 泽楷,打造绿色未来更多的时候,泽楷集团是低调的`,所以,在沸沸扬扬的媒体上,你很难看到泽楷的报道.然...
说通假 说通假同源字之间由于意义相通而可以通用,假借字由于与所记录的词在意义上没有联系,只能称为假借,二者的...
毕业论文开题报告 毕业论文开题报告范文毕业论文开题报告范文1  1.开题报告(含“文献综述”)作为毕业设计(论文)答辩...
设计心理学的论文 关于设计心理学的论文(通用10篇)  设计心理学是设计专业一门理论课,是设计师必须掌握的学科,是建立...
无人机遥感技术现状与应用 无人机遥感技术现状与应用随着测绘科学技术的发展,各行各业对遥感数据的需求日益增加,但遥感数据获取手段...
浅谈如何激发学生的学习动机论... 浅谈如何激发学生的学习动机论文  学习动机是指推动和维持学习的心理动因。产生学习动机的基础是学习需要...