zoukankan      html  css  js  c++  java
  • 编写高质量的Python代码系列(一)之用Pythonic方式来思考

      Python开发者用Pythonic这个形容词来描述具有特定风格的代码。这种风格是大家在使用Python语言进行编程并相互协作的过程中逐渐形成的习惯。那么,如何以改风格完成常见的Python编程工作呢?本节将会回答这个问题。

    • 第一条:确认自己所用的Python版本

    • 第二条:遵循PEP8风格指南

    • 第三条:了解bytes、str与unicode的区别

    • 第四条:用辅助函数来取代复杂的表达式

    • 第五条:了解切割序列的方法

    • 第六条:在单次切片操作内,不要同时指定start、end和stride

    • 第七条:用列表推导来取代map和filter

    • 第八条:不要使用含有两个以上表达式的列表推导

    • 第九条:用生成器表达式来改写数据量较大的列表推导

    • 第十条:尽量用enumerate取代range

    • 第十一条:用zip函数同时遍历两个迭代器

    • 第十二条:不要在for和while循环后面写else块

    • 第十三条:合理利用try/exceot/else/finally结构中的每个代码块

    第一条:确认自己所用的Python版本

    C:UsersfeiDesktop>python --version
    Python 3.6.5
    
    import sys
    sys.version
    Out[14]: '3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)]'

     要点

    •    要点1:有两个版本的Python处于活跃状态,它们是:Python2和Python3

    •    要点2:有很多中流行的Python运行时环境,例如: Cpython, Jython,IronPython以及PyPy等

    •    要点3:在操作系统的命令行中运行Python时,请确保该Python的版本与你想使用的Python版本相符。

    •    要点4:由于Python社区把开发重点放在Python3上,所以在开发后序项目时,应该优先考虑采用Python3

    第二条:遵循PEP8风格指南

     《Python Enhancement Proposal #8》(8号Python增强提案)又叫PEP8,它是针对Python代码格式而编订的风格指南。尽管可以在保证语法正确的前提下随意编写Python代码,但是采用一致的风格书写代码可以令代码更加易懂、更加易读。

    完整版PEP8指南

    1. PEP 8 -- Style Guide for Python Code

    2. PEP8中文翻译

    下面列出几条绝对应该遵守的规则

    空白: Python中的空白( whitespace)会影响代码的含义。 Python程序员使用空白 的时候尤其在意,因为它们还会影响代码的清晰程度。

    • 使用 space(空格)来表示缩进,而不要用tab(制表符)。

    • 和语法相关的每一层缩进都用4个空格来表示。

    • 每行的字符数不应超过79。

    • 对于占据多行的长表达式来说,除了首行之外的其余各行都应该在通常的缩进 级别之上再加4个空格。

    • 文件中的函数与类之间应该用两个空行隔开。

    • 在同一个类中,各方法之间应该用一个空行隔开。

    • 在使用下标来获取列表元素、调用函数或给关键字参数赋值的时候,不要在两 旁添加空格。

    • 为变量赋值的时候,赋值符号的左侧和右侧应该各自写上一个空格,而且只写 个就好。

    命名:PEP8提倡采用不同的命名风格来编写 Python代码中的各个部分,以便在阅 读代码时可以根据这些名称看出它们在 Python语言中的角色。

    • 函数、变量及属性应该用小写字母来拼写,各单词之间以下划线相连,例如lowercase_underscore。

    • 受保护的实例属性,应该以单个下划线开头,例如, leading underscore

    • 私有的实例属性,应该以两个下划线开头,例如, double leading underscore

    • 类与异常,应该以每个单词首字母均大写的形式来命名,例如, Capitalized Word 口模块级别的常量,应该全部采用大写字母来拼写,各单词之间以下划线相连, 例如, ALL CAPS。

    • 类中的实例方法( instance method)。应该把首个参数命名为sef,以表示该对象自身

    • 类方法( lass method)的首个参数,应该命名为cbs,以表示该类自身。

    表达式和语句:( The Zen of Python)( Python之禅)中说:“每件事都应该有直白的做 法,而且最好只有一种。”PEP8在制定表达式和语句的风格时,就试着体现了这种思想。

    • 采用内联形式的否定词,而不要把否定词放在整个表达式的前面,例如,应该 写 if a is not b而不是 if not a is b。

    • 不要通过检测长度的办法(如 if len(somelist)=0)来判断 somalis是否为目 或”等空值,而是应该采用 if not somelist这种写法来判断,它会假定:空值将 自动评估为 False

    • 检测 somalis是否为]或hi等非空值时,也应如此, if somelist语句默认会把 非空的值判断为Tme。

    • 不要编写单行的i语句、for循环、 while循环及cxp复合语句,面是应该把 这些语句分成多行来书写,以示清晰。

    • import语句应该总是放在文件开头。

    • 引入模块的时候,总是应该使用绝对名称,而不应该根据当前模块的路径来 使用相对名称。例如,引入bar包中的foo模块时,应该完整地写出 from bar import foo,面不应该简写为 import foo。

    • 如果一定要以相对名称来编写ipon语句,那就采用明确的写法: from import food 口文件中的那些mpon语句应该按顺序划分成三个部分,分别表示标准库模块 第三方模块以及自用模块:在每一部分之中,各如m语句应该按模块的字母 顺序来排列。

    要点

    • 要点1:当编写Python代码时,总是应该遵循PEP8风格指南。

    • 要点2:与广大Python开发者采用同一套代码风格,可以使项目更利于多人协作。

    • 要点3: 采用一致的风格来编写代码,可以令后续的修改工作变得更为容易。

    第三条:了解bytes、str与unicode的区别

     Python3有两种表示字符序列的类型:bytes和str。前者的实例包含原始的8位值;后者的实例包含Unicide字符。

     Python2也有两种表示字符序列的类型,分别叫做str和unicode。与Python3不同的是,str的实例包含原始的8位值;而unicode的实例,则包含Unicode字符。

    注:unicode --> 二进制     encode方法

      二进制  --> unicode    decode方法

    要点:

    • 要点1:在Python3中,bytes是一种包含8位值的序列,str是一种包含unicoe字符的序列。开发者不能以>或+等操作符来混同操作bytes和str实例

    • 要点2:在Python2中,str是一种包含8位值的序列,unicode是一种包含Unicode字符的序列。如果str只包含有7位ASCII字符,那么可以通过相关额操作符同时使用str与unicode。

    • 要点3:在对输入的数据进行操作之前,使用辅助函数来保证字符序列的类型与开发者的期望相符(有的时候,开发者想操作以UTF-8格式来编码的8位值,有的时候,则想操作Unicode字符)。

    • 要点4:从文件中读取二进制数据,或向其中写入二进制数据时,总应该以‘rb'或‘wb’等二进制模式来开启文件。

    第四条:用辅助函数来取代复杂的表达式

     要点:

    • 要点1:开发者很容易过度运用Python的语法特性,从而写出那种特别复杂并且难以理解的单行表达式

    • 要点2:请把复杂的表达式移入辅助函数之中,如果要反复使用相同的逻辑,那就更应该这样做

    • 要点3:使用if/else表达式,要比用or或and这样的Boolean操作符写成的表达式更加清晰。

    第五条:了解切割序列的方法

     要点:

    • 要点1:不要写多余的代码:当start索引为0,或end索引为序列长度时,应该将其省略。

    • 要点2:切片操作不会计较start与end索引是否越界,这使得我们很容易就能从序列的前端或后端开始,对其进行范围固定的切片操作(如a[:20]或a[-20:])。

    • 要点3:对list赋值的时候,如果使用切片操作,就会把原列表处在相关范围内的值替换成新值,即使他们的长度不同也依然可以替换。

    • 要点4:如果对赋值操作右侧的列表使用切片,而把切片的起止索引都留空,那就会产生一份原列表的拷贝。
    • 要点5:如果对赋值左侧的列表使用切片,而又没有指定起止索引,那么系统会把右侧的新值复制一份,并好用这份拷贝替换左侧列表的全部内容,而不会重新分配新的列表。

    示例代码

    a = [1,2,3,4,5]
    b = a
    id(a)
    Out[4]: 588602575880
    id(b)
    Out[5]: 588602575880
    c = a[:]
    id(c)
    Out[7]: 588602487112
    a
    Out[8]: [1, 2, 3, 4, 5]
    id(a)
    Out[9]: 588602575880
    a[2:4] = [10,11]
    id(a)
    Out[11]: 588602575880
    a
    Out[12]: [1, 2, 10, 11, 5]
    a[2:4] = [2,2,2,2,2]
    a
    Out[14]: [1, 2, 2, 2, 2, 2, 2, 5]
    id(a)
    Out[15]: 588602575880

    第六条:在单次切片操作内,不要同时指定start、end和stride

     要点

    • 要点1:既有start和end,又有stride的切割操作,可能会令人费解。

    • 要点2:尽量使用stride为整数,切不带start或end索引的切割操作。尽量避免用负数做sride.

    • 要点3:再同一个切片操作内,不要同时使用start、end和stride。如果确实需要执行这种操作,那就考虑将其拆解为两条赋值语句,其中一角做范围切割,另一条做步进切割,或考虑使用内置函数itertools模块中的islice。

    第七条:用列表推导来取代map和filter

    •  要点1: 列表推导比内置的map和filter函数清晰, 因为它无需额外编写lanbda表达式

    •  要点2:  列表推导可以跳过输入列表中的某些元素,如果改用map来做,那就必须辅以filter方能实现

    •  要点3:字典和集合也支持列表推导

    第八条:不要使用含有两个以上表达式的列表推导

    •  要点1: 列表推导支持多级循环,每一级循环也支持多项条件

    •  要点2: 超过两个表达式的列表推导式很难理解的,应该尽量避免

    第九条:用生成器表达式来改写数据量较大的列表推导

    •  要点1: 当输入数据量较大时,列表推导可能会因为占用太多内存而出问题

    •  要点2: 由生成器表达式所返回的迭代器,可以逐次产生输出值,从而避免了内存用量问题。

    •  要点3: 把某个生成器表达式所返回的迭代器,放在另一个生成器表达式的for子表达式中,即可将二者组合起来

    •  要点4: 串在一起的生成器表达式执行速度很快。

    第十条:尽量用enumerate取代range

    •  要点1:enumerate函数提供了一种精简的写法,可以在遍历迭代器时获知每个元素的索引

    •  要点2: 尽量用enumerate来改写那种将range与下标访问相结合的序列遍历代码

    •  要点3: 可以给enumerate提供第二个参数,已制定开始技术是所用的值(默认为0) 

    第十一条:用zip函数同时遍历两个迭代器

    •  要点1: 内置的zip函数可以平行的遍历多个迭代器

    •  要点2: Python3中的zip相当于生成器,会在遍历过程中逐次产生元组,而Python2中的zip则是直接把元组完全生成好,并一次性返回整份列表。

    •  要点3: 如果提供的迭代器长度不等,那么zip就会自动提前终止

    •  要点4:itertools内置模块中的zip_longest函数可以平行地遍历多个迭代器,而不用在乎它们的长度是否相等

    第十二条:不要在for和while循环后面写else块

    •  要点1: Python有种也属于语法,可在for及while循环的内部语句块之后紧跟一个else块

    •  要点2: 只有当整个循环主体都没遇到break语句时,循环后面的else块才会执行

    • 要点3: 不要在循环后面使用else块,因为这种写法既不直观,又容易引起误解 

    第十三条:合理利用try/exceot/else/finally结构中的每个代码块

    • 要点1: 无论try块是否发生异常,都可利用try/finally复合语句中的finally块来执行清理工作

    • 要点2: else块可以用来缩减try块中的代码量,并把没有发生异常时所要执行的语句与try/except代码块隔开

    • 要点3: 顺利运行try块后,若想使某些操作能在finally块的清理代码之前执行,则可将这些操作写到else块中。 

  • 相关阅读:
    设计模式
    python高亮显示输出
    数据库入门3 数据库设计
    分支循环相关
    软件目录结构规范
    数据库入门2 数据库基础
    三级菜单
    字符串相关问题
    登录接口
    购物车
  • 原文地址:https://www.cnblogs.com/zhangyafei/p/10752138.html
Copyright © 2011-2022 走看看