热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python可读性好吗_教你写出可读性高的Python代码

作者:Python最佳实践指南2018来源:https:learnku.comdocspython-guide2018writing-style326

作者: Python 最佳实践指南 2018

来源:https://learnku.com/docs/python-guide/2018/writing-style/3261

如果有人问起 Python 程序员他们最喜欢 Python 哪一点,他们一定会提到 Python 的高可读性。确实,对于 Python 来说,其高可读性一直是 Python 这门语言设计的核心。一个不争的事实是,相对于写代码而言,读代码才是更加平常的事情。

Python 代码有高可读性的一个原因就是其有着相对而言更加完善的编码风格准则和 「Python 化」习惯语法。

当 Python 老手(Pythonista)认为一段代码不「Python 化」,他们通常的意思是这段代码没有遵循一般准则,同时亦没有以最佳的(最具可读性的)方式表达出代码的意图。

在一些极端的情况下,没有公认最佳的方式来表达 Python 代码的意图,不过这种极端情况非常罕见。

一般概念

明确代码意义

尽管 Python 可以写出从各种意义上来说都像是黑魔法的代码,但最简单直白的表达才是正道。

不优雅def make_complex(*args):

x, y = args

return dict(**locals())

优雅def make_complex(x, y):

return {'x': x, 'y': y}

在上述好的代码中,x 和 y 清晰明了的从参数中获取值,并清晰明了的返回了一个字典。当开发者看到这个函数后就可以明了这个函数的用途,而不好的代码则不行。

一行一个声明语句

虽然在 Python 中我们推崇使用形如列表生成式这种简洁明了的复合语句,但是除此以外,我们应该尽量避免将两句独立分割的代码写在同一行。

不好的风格print 'one'; print 'two'

if x == 1: print 'one'

if and :

# do something

好的风格print 'one'

print 'two'

if x == 1:

print 'one'

cond1 =

cond2 =

if cond1 and cond2:

# do something

函数的参数

函数的参数可以使用四种不同的方式传递给函数。必选参数 是没有默认值的必填的参数。必选参数是最简单的参数构成,用于参数较少的函数的构成,是该函数意义的一部分,使用他们的顺序是按照定义自然排序的。举个例子,对于 send(message, recipient) 和 point(x, y) 这两个函数,使用函数的人需要知道这个函数需要两个参数,并且记住两个参数的顺序。

在调用函数的时候,我们也可以使用参数的名称调用。使用参数的名称的方式可以调换参数的顺序,就像 send(recipient='World',message='Hello') 和 point(y=2, x=1) 这样。但这样的做法会降低代码的可读性,并且使代码冗长,因此更建议使用 send('Hello', 'World') 和 point(1,2) 这样的方式调用。关键字参数 是非强制的,且有默认值。它们经常被用在传递给函数的可选参数中。当一个函数有超过两个或三个位置参数时,函数签名会变得难以记忆,使用带有默认参数的关键字参数有时候会给你带来便利。比如,一个更完整的 send 函数可以被定义为 send(message, to, cc=None, bcc=None)。这里的 cc 和 bcc 是可选的, 当没有传递给它们其他值的时候,它们的值就是 None。

Python 中有多种方式调用带关键字参数的函数。比如说,我们可以按照定义时的参数顺序而无需明确的命名参数来调用函数,就像 send('Hello', 'World', 'Cthulhu', 'God') 是将密件发送给上帝。我们也可以使用命名参数而无需遵循参数顺序来调用函数,就像 send('Hello again', 'World', bcc='God', cc='Cthulhu') 。没有特殊情况的话,这两种方式都需要尽力避免,最优的调用方式是与定义方式一致:send('Hello', 'World', cc='Cthulhu',bcc='God') 。

作为附注,请遵循 YAGNI 原则。通常,移除一个用作『以防万一』但从未使用的可选参数(以及它在函数中的逻辑),比添加一个所需的新的可选参数和它的逻辑要来的困难。任意参数列表 是第三种给函数传参的方式。如果函数的参数数量是动态的,该函数可以被定义成 *args 的结构。在这个函数体中, args 是一个元组,它包含所有剩余的位置参数。举个例子, 我们可以用任何容器作为参数去调用 send(message, *args) ,比如 send('Hello', 'God', 'Mom','Cthulhu')。在此函数体中, args 相当于 ('God','Mom', 'Cthulhu')。

然而,这种结构有一些缺点,使用时应该特别注意。如果一个函数接受的参数列表具有相同的性质,通常把它定义成一个参数,这个参数是一个列表或者其他任何序列会更清晰。在这里,如果 send 参数有多个容器(recipients),将之定义成 send(message,recipients) 会更明确,调用它时就使用 send('Hello', ['God', 'Mom', 'Cthulhu'])。这样的话, 函数的使用者可以事先将容器列表维护成列表(list)形式,这为传递各种不能被转变成其他序列的序列(包括迭代器)带来了可能。任意关键字参数字典 是最后一种给函数传参的方式。如果函数要求一系列待定的命名参数,我们可以使用 **kwargs 的结构。在函数体中, kwargs 是一个字典,它包含所有传递给函数但没有被其他关键字参数捕捉的命名参数。

和 任意参数列表 中所需注意的一样,相似的原因是:这些强大的技术在非特殊情况下,都要尽量避免使用,因为其缺乏简单和明确的结构来足够表达函数意图。

编写函数的时候采用何种参数形式,是用位置参数,还是可选关键字参数,是否使用形如任意参数 的高级技术,这些都由程序员自己决定。如果能明智地遵循上述建议,即可轻松写出这样的 Python 函数:易读(名字和参数无需解释)

易改(添加新的关键字参数不会破坏代码的其他部分)

避免魔法方法

Python 对骇客来说是一个强有力的工具,它拥有非常丰富的钩子(hook)和工具,允许你施展几乎任何形式的技巧。比如说,它能够做以下:改变对象创建和实例化的方式;

改变 Python 解释器导入模块的方式;

甚至可能(如果需要的话也是被推荐的)在 Python 中嵌入 C 程序。

尽管如此,所有的这些选择都有许多缺点。使用最直接的方式来达成目标通常是最好的方法。它们最主要的缺点是可读性不高。许多代码分析工具,比如说 pylint 或者 pyflakes,将无法解析这种『魔法』代码。

我们认为 Python 开发者应该知道这些近乎无限的可能性,因为它为我们灌输了没有不可能完成的任务的信心。然而,知道何时 不能 使用它们也是非常重要的。

就像一位功夫大师,一个 Pythonista 知道如何用一个手指杀死对方,但从不会那么去做。

我们都是负责任的用户

如前所述,Python 允许很多技巧,其中一些具有潜在的危险。一个好的例子是:任何客户端代码能够重写一个对象的属性和方法(Python 中没有 private 关键字)。这种哲学是在说:『我们都是负责任的用户』,它和高度防御性的语言(如 Java,拥有很多机制来预防错误操作)有着非常大的不同。

这并不意味着,比如说,Python 中没有属性是私有的,也不意味着没有合适的封装方法。与其依赖在开发者的代码之间树立起的一道道隔墙,Python 社区更愿意依靠一组约定,来表明这些元素不应该被直接访问。

私有属性的主要约定和实现细节是在所有的 内部 变量前加一个下划线。如果客户端代码打破了这条规则并访问了带有下划线的变量,那么因内部代码的改变而出现的任何不当的行为或问题,都是客户端代码的责任。

鼓励大方地使用此约定:任何不开放给客户端代码使用的方法或属性,应该有一个下划线前缀。这将保证更好的职责划分以及更容易对已有代码进行修改。将一个私有属性公开化总是可能的,但是把一个公共属性私有化可能是一个更难的选择。

返回值

当一个函数变得复杂,在函数体中使用多返回值的语句并不少见。然而,为了保持函数的可读性,建议在函数体中避免使用返回多个有意义的值。

在函数中返回结果主要有两种情况:函数正常运行并返回它的结果,以及错误的情况,要么因为一个错误的输入参数,要么因为其他导致函数无法完成计算或任务的原因。

如果你在面对第二种情况时不想抛出异常,返回一个值(比如说 None 或 False )来表明函数无法正确运行,可能是需要的。在这种情况下,越早返回所发现的不正确上下文越好。这将帮助扁平化函数的结构:我们假定在『因为错误而返回』的语句后的所有代码都能够满足函数主要结果运算。这种类型的多发挥结果,是有必要的。

然而,当一个函数在其正常运行过程中有多个主要出口点时,它会变得难以调试其返回结果,所以保持单个出口点可能会更好。这也将有助于提取某些代码路径,而且多个出口点很有可能意味着这里需要重构:def complex_function(a, b, c):

if not a:

return None # 抛出一个异常可能会更好

if not b:

return None # 抛出一个异常可能会更好

# 一些复杂的代码试着用 a,b,c 来计算x

# 如果成功了,抵制住返回 x 的诱惑

if not x:

# 使用其他的方法来计算出 x

return x # 返回值 x 只有一个出口点有利于维护代码

习语(Idiom)

编程习语,说得简单些,就是写代码的 方式。编程习语的概念在 c2 和 Stack Overflow 上有详尽的讨论。

符合习语的 Python 代码通常被称为 Pythonic。

通常只有一种、而且最好只有一种明显的方式去编写代码。对 Python 初学者来说,无意识的情况下很少能写出习语式 Python 代码,所以应该有意识地去获取习语的书写方式。

如下有一些常见的 Pythonic:

解包(Unpacking)

如果你知道一个列表或者元组的长度,你可以将其解包并为它的元素取名。比如,enumerate() 会对 list 中的每个项提供包含两个元素的元组:for index, item in enumerate(some_list):

# do something with index and item

你也能通过这种方式交换变量:a, b = b, a

嵌套解包也能工作:a, (b, c) = 1, (2, 3)

Python 3 提供了扩展解包的新方法在 PEP 3132 有介绍:a, *rest = [1, 2, 3]

# a = 1, rest = [2, 3]

a, *middle, c = [1, 2, 3, 4]

# a = 1, middle = [2, 3], c = 4

创建一个被忽略的变量

如果你需要赋值(比如,在 解包(Unpacking) )但不需要这个变量,请使用 __:filename = 'foobar.txt'

basename, __, ext = filename.rpartition('.')注意

许多 Python 风格指南建议使用单下划线的 _ 而不是这里推荐的双下划线 __ 来标记废弃变量。问题是, _常用在作为 gettext() 函数的别名,也被用在交互式命令行中记录最后一次操作的值。相反,使用双下划线 十分清晰和方便,而且能够消除使用其他这些用例所带来的意外干扰的风险。

创建一个含 N 个对象的列表

使用 Python 列表中的 * 操作符:four_nones = [None] * 4

创建一个含 N 个列表的列表

因为列表是可变的,所以 * 操作符(如上)将会创建一个包含 N 个且指向 同一个 列表的列表,这可能不是你想用的。取而代之,请使用列表解析:four_lists = [[] for __ in xrange(4)]

注意:在 Python 3 中使用 range() 而不是 xrange()。

根据列表来创建字符串

创建字符串的一个常见习语是在空的字符串上使用 str.join() :letters = ['s', 'p', 'a', 'm']

word = ''.join(letters)

这会将 word 变量赋值为 spam。这个习语可以用在列表和元组中。

在集合体(collection)中查找一个项

有时我们需要在集合体中查找。让我们看看这两个选择,列表和集合(set),用如下代码举个例子:s = set(['s', 'p', 'a', 'm'])

l = ['s', 'p', 'a', 'm']

def lookup_set(s):

return 's' in s

def lookup_list(l):

return 's' in l

即使两个函数看起来完全一样,但因为 查找集合 是利用了 Python 中的『集合是可哈希』的特性,两者的查询性能是非常不同的。为了判断一个项是否在列表中,Python 将会查看每个项直到它找到匹配的项。这是耗时的任务,尤其是对长列表而言。另一方面,在集合中, 项的哈希值将会告诉 Python 在集合的哪里去查找匹配的项。结果是,即使集合很大,查询的速度也很快。在字典中查询也是同样的原理。想了解更多内容,请见 StackOverflow 。想了解在每种数据结构上的多种常见操作的花费时间的详细内容, 请见 此页面。

因为这些性能上的差异,在下列场景中,使用集合或者字典而不是列表,通常会是个好主意:集合体中包含大量的项;

你将在集合体中重复地查找项;

你没有重复的项。

对于小的集合体、或者你不会频繁查找的集合体,建立哈希带来的额外时间和内存的开销经常会大过改进搜索速度所节省的时间。

约定

这里有一些你应该遵循的约定,以让你的代码更加易读。

检查变量是否等于常量

你不需要明确地比较一个值是 True,或者 None,或者 0 - 你可以仅仅把它放在 if 语句中。参阅 真值测试 来了解什么被认为是 false:

糟糕:if attr == True:

print 'True!'

if attr == None:

print 'attr is None!'

优雅:# 检查值

if attr:

print 'attr is truthy!'

# 或者做相反的检查

if not attr:

print 'attr is falsey!'

# 或者,None 等于 false,你可以直接相较它进行匹配

if attr is None:

print 'attr is None!'

访问字典元素

不要使用 dict.has_key() 方法。相反,使用 x in d 语法,或者将默认参数传递给 dict.get() 方法。

坏的示例:d = {'hello': 'world'}

if d.has_key('hello'):

print d['hello'] # prints 'world'

else:

print 'default_value'

推荐的示例:d = {'hello': 'world'}

print d.get('hello', 'default_value') # prints 'world'

print d.get('thingy', 'default_value') # prints 'default_value'

# 或者:

if 'hello' in d:

print d['hello']

操作列表的简便方法

列表推导式 提供了一个强大并且简洁的方法来对列表价进行操作。除此之外,map() 和 filter() 函数在列表的操作上也是非常简洁的。

坏:# Filter elements greater than 4

a = [3, 4, 5]

b = []

for i in a:

if i > 4:

b.append(i)

好:a = [3, 4, 5]

b = [i for i in a if i > 4]

# Or:

b = filter(lambda x: x > 4, a)

坏:# Add three to all list members.

a = [3, 4, 5]

for i in range(len(a)):

a[i] += 3

好:a = [3, 4, 5]

a = [i + 3 for i in a]

# Or:

a = map(lambda i: i + 3, a)

使用 enumerate() 来跟踪正在被处理的元素索引。a = [3, 4, 5]

for i, item in enumerate(a):

print i, item

# prints

# 0 3

# 1 4

# 2 5

比起手动计数,使用 enumerate() 函数有更好的可读性,而且,他更加适合在迭代器中使用。

读文件

使用 with open 语法来读文件,它能够为你自动关闭文件。

坏:f = open('file.txt')

a = f.read()

print a

f.close()

好:with open('file.txt') as f:

for line in f:

print line

即使在 with 控制块中出现了异常,它也能确保你关闭了文件,因此,使用 with 语法是更加优雅的。

行的延续

当一个代码逻辑行的长度超过可接受的限度时,你需要将之分为多个物理行。如果行的结尾是一个反斜杠,Python 解释器会把这些连续行拼接在一起。这在某些情况下很有帮助, 但我们总是应该避免使用,因为它的脆弱性:如果在行的结尾,在反斜杠后加了空格,这会破坏代码,而且可能有意想不到的结果。

一个更好的解决方案是在元素周围使用括号。左边以一个未闭合的括号开头,Python 解释器会把行的结尾和下一行连接起来直到遇到闭合的括号。同样的行为适用中括号和大括号。

糟糕:my_very_big_string = """For a long time I used to go to bed early. Sometimes,\

when I had put out my candle, my eyes would close so quickly that I had not even\

time to say "I'm going to sleep.""""

from some.deep.module.inside.a.module import a_nice_function, another_nice_function,\

yet_another_nice_function

优雅:my_very_big_string = (

"For a long time I used to go to bed early. Sometimes, "

"when I had put out my candle, my eyes would close so quickly "

"that I had not even time to say "I'm going to sleep.""

)

from some.deep.module.inside.a.module import (

a_nice_function, another_nice_function, yet_another_nice_function)

尽管如此,通常情况下,必须去分割一个长逻辑行意味着你同时想做太多的事,这可能影响可读性。



推荐阅读
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 热血合击脚本辅助工具及随机数生成器源码分享
    本文分享了一个热血合击脚本辅助工具及随机数生成器源码。游戏脚本能够实现类似真实玩家的操作,但信息量有限且操作不可控。热血合击脚本辅助工具可以帮助玩家自动刷图、换图拉怪等操作,并提供了雷电云手机的扩展服务。此外,还介绍了使用mt_rand函数作为随机数生成器的代码示例。 ... [详细]
  • Linux如何安装Mongodb的详细步骤和注意事项
    本文介绍了Linux如何安装Mongodb的详细步骤和注意事项,同时介绍了Mongodb的特点和优势。Mongodb是一个开源的数据库,适用于各种规模的企业和各类应用程序。它具有灵活的数据模式和高性能的数据读写操作,能够提高企业的敏捷性和可扩展性。文章还提供了Mongodb的下载安装包地址。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • GPT-3发布,动动手指就能自动生成代码的神器来了!
    近日,OpenAI发布了最新的NLP模型GPT-3,该模型在GitHub趋势榜上名列前茅。GPT-3使用的数据集容量达到45TB,参数个数高达1750亿,训练好的模型需要700G的硬盘空间来存储。一位开发者根据GPT-3模型上线了一个名为debuid的网站,用户只需用英语描述需求,前端代码就能自动生成。这个神奇的功能让许多程序员感到惊讶。去年,OpenAI在与世界冠军OG战队的表演赛中展示了他们的强化学习模型,在限定条件下以2:0完胜人类冠军。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
  • 2016 linux发行版排行_灵越7590 安装 linux (manjarognome)
    RT之前做了一次灵越7590黑苹果炒作业的文章,希望能够分享给更多不想折腾的人。kawauso:教你如何给灵越7590黑苹果抄作业​zhuanlan.z ... [详细]
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
author-avatar
资深化妆师May
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有