原文链接:http://blog.guoyb.com/2016/12/03/bad-py-style/

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

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

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

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

先看错误示范吧:

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
[1]
2
[1, 2]

理解这其中的原因,最重要的是有两点:
函数本身也是一个对象,默认参数绑定于这个函数对象上
append 这类方法会直接修改对象,所以下次调用此函数时,其绑定的默认参数已经不再是空list了

正确的做法如下:

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的冗余代码

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

d = {}
datas = [1, 2, 3, 4, 2, 3, 4, 1, 5]
for k in datas:if k not in d:d[k] = 0 d[k] += 1

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

default_d = defaultdict(lambda: 0)
datas = [1, 2, 3, 4, 2, 3, 4, 1, 5]
for k in datas:default_d[k] += 1

同样的,这样的代码:

# d is a dict
if 'list' not in d:
d['list'] = []
d['list'].append(x)

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

# d is a dict
d.setdefault('list', []).append(x)

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

# d is a dict
for k in d:
v = d[k]
# do something# l is a list
for i in len(l):
v = l[i]
# do something

应该用更 pythonic 的写法:

# 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语句

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

search_list = ['Jone', 'Aric', 'Luise', 'Frank', 'Wey']
found = False
for s in search_list:if s.startswith('C'):found = True# do something when foundprint('Found')breakif not found:# do something when not foundprint('Not found')

其实,用 for…else 更优雅:

search_list = ['Jone', 'Aric', 'Luise', 'Frank', 'Wey']
for s in search_list:if s.startswith('C'):# do something when foundprint('Found')break
else:# do something when not foundprint('Not found')

过度使用 tuple unpacking

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

# human = ('James', 180, 32)
name,height,age = human

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

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

# human = ('James', 180, 32)
name,height,age, _ = human
# or
# name, height, age, sex = human

有如下几种方式解决这一问题:
老老实实写 name=human[0] 这种代码,在需要使用性别信息处加上 sex=human[3]
使用 dict 来表示 human
使用 namedtuple

# human = namedtuple('human', ['name', 'height', 'age', 'sex'])
h = human('James', 180, 32, 0)
# 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 代码都能做到比较好的规范。但是,不严格要求自己,也很容易就写出犯二的代码。上面列出的只是很小的一部分,唯有多读、多写、多想,才能培养敏锐的代码嗅觉,第一时间发现坏味道啊。欢迎大家补充~

转载于:https://www.cnblogs.com/Detector/p/8652968.html

【Python】远离 Python 最差实践,避免挖坑相关推荐

  1. 《Python编程从入门到实践》袁国忠 译 P1~P200学习笔记

    <Python编程从入门到实践>袁国忠 译 P1~P200 学习笔记 <Python编程从入门到实践>袁国忠 译 P1~P200之前两天在学习时做的笔记总结,在这里也记录一下, ...

  2. python按指定概率抽样_基于python进行抽样分布描述及实践详解

    本次选取泰坦尼克号的数据,利用python进行抽样分布描述及实践. 备注:数据集的原始数据是泰坦尼克号的数据,本次截取了其中的一部分数据进行学习.Age:年龄,指登船者的年龄.Fare:价格,指船票价 ...

  3. python编程入门指南 明日科技-python从入门到项目实践明日科技三剑客书籍视频...

    ┃ code(实例源码) ┃ ┃ 03.rar ┃ ┃ 04.rar ┃ ┃ 05.rar ┃ ┃ 06.rar ┃ ┃ 07.rar ┃ ┃ 08.rar ┃ ┃ 09.rar ┃ ┃ 10.rar ...

  4. python编程 从入门到实践怎么样-python编程从入门到实践这本书怎么样

    <Python编程-从入门到实践>作者: Eric Matthes,已翻译为中文,人民邮电出版社出版. python编程从入门到实践怎么样? 我们一起看看已经学习的同学对这本书的口碑和评价 ...

  5. python编程入门指南怎么样-python编程从入门到实践这本书怎么样

    <Python编程-从入门到实践>作者: Eric Matthes,已翻译为中文,人民邮电出版社出版. python编程从入门到实践怎么样? 我们一起看看已经学习的同学对这本书的口碑和评价 ...

  6. python编程 入门到实践-终于懂了python编程从入门到实践

    Python语言是一种典型的脚本语言,简洁,语法约束少,接近人类语言.有丰富的数据结构,例如列表.字典.集合等.具有可移植性,支持面向过程和面向对象编程,并且开源.以下是小编为你整理的python编程 ...

  7. python编程 从入门到实践豆瓣-三周刷完《Python编程从入门到实践》的感受

    本文将以对话采访的形式展现 为什么会选择学习Python 语法简洁,实用,和golang部分类似,学习性价比高: 应用范围广,涉及后端,机器学习,图像处理,游戏等: 好奇这是一门怎样的语言 计划如何学 ...

  8. python入门到实践-Python编程从入门到实践(基础入门)

    Python编程从入门到实践-------基础入门 1.Python中的变量 2.Python首字母大写使用title()方法,全部大写upper()方法,全部小写lower()方法 3.Python ...

  9. python基础学习[python编程从入门到实践读书笔记(连载五)]:数据可视化项目第16章

    文章目录 下载数据 制作全球地震散点图:JSON格式 end 几个实验结果: 每日最高气温: 地震图绘制: 下载数据 CSV文件格式 在文本文件中存储数据,一个简单方式是将数据作为一系列以逗号分隔的值 ...

最新文章

  1. python读取文件读不出来-python文件读取失败怎么处理
  2. QT官方第三方开源工具
  3. html 连续发送数据库,不要在循环中对数据库进行操作.htm
  4. 【视频基础】封装格式和编码格式
  5. GraphQL入门之分析RESTful存在的问题
  6. 内核中 subsys_initcall 以及初始化标号
  7. 【干货】用户画像和精准化平台系统实践.pdf(附下载链接)
  8. 读取指定文件夹限定文件
  9. 如何在iOS 9中启用“应用程序传输安全性”的情况下加载HTTP URL? [重复]
  10. 第一次点击button, view视图出现;第二次点击button,view视图消失
  11. 用极大似然法估计因子载荷矩阵_因子分析的方法和公式 - Minitab
  12. 简述对互联网产品经理的认识
  13. 不能创建对象qmdispatch_按键精灵更新时提示 ActiveX 部件不能创建对象 错误代码 800a01ad_电脑故障...
  14. 页面监听-iframe子父页面信息传递与监听
  15. srp——点光源阴影的一些坑总结
  16. Qt运行程序弹出异常窗口解释
  17. Linux基础知识学习笔记
  18. matlab向excel连续写数据,每次换行
  19. 考研计划安排表怎么在手机便签上记录?
  20. 定时开关机-Android4.4/6.0

热门文章

  1. url 编码 与 接口签名
  2. Angular——单页面与路由的使用
  3. 支撑计算机高速化的半导体技术
  4. 二叉树的深度优先和广度优先遍历
  5. java请模拟出双重定时器_Java多线程基础 - osc_czmaebyq的个人空间 - OSCHINA - 中文开源技术交流社区...
  6. P1216 [IOI1994][USACO1.5]数字三角形 Number Triangles(简单dp)
  7. linux时间调整为dst,Linux上系统时间函数、DST等相关有关问题总结
  8. 函数计算搭建小程序Web应用后端服务
  9. 封神系统-运维大脑的日志检测
  10. 如何做一场高质量的分享?