编写高质量的python(第一章)
来源:互联网 发布:知乎怎么发专栏 编辑:程序博客网 时间:2024/06/03 11:52
第一章 用pythonic方式来思考
- 第一章 用pythonic方式来思考
- 第四条用辅助函数来取代复杂的表达式
- 第五条了解切割序列的办法
- 第六条在单次切片操作内不要同时指定startendstride
- 第七条用列表来取代map和filter
- 第八条不要使用含有两个以上表达式的列表推导
- 第九条用生成器表达式来改写数据量较大的列表推导
- 第十条尽量用enumerate 取代 range
- 第十一条用zip函数同时遍历两个迭代器
- 第十二条不要再for 和while循环后面写else块
- 第十三条合理利用tryexceptelsefinally结构中的每个代码块
- 理解Pythonic概念:
def quicksort(array): less = [];greater=[] if len (array) <=1: return array pivot = array.pop() for i in array: if x <= pivot:less.append(x) else: greater.append(x) return quciksort(less)+[pivot]+quciksort(greater)
简洁的代码风格:a,b=b,a
;简洁的遍历。简洁的SLice语法
编写pythonic代码
(1)避免劣化代码:
避免用大小写来区分不同对象
避免使用容易混淆的名称
- 不要害怕太长的变量名
(2)深入认识python………
PEP检查代码规范。。。。(不如pycharm)
pip install -U pep8pep8 --first 文件名.py
理解python与C语言的不同之处
(1)“缩进”与”{}”
(2)’与”
(3)三元操作符?:[ python中 还可以表示为
x if c else b
](4)switch..case[python没有,那又怎样?]
在代码中适当添加注释
- 使用块或者行注释的时候注释复杂操作和算法
- 注释和代码隔开一定距离,同时在块注释之后最好多留几行空白写代码。
- 给外部访问的函数和方法添加文档注释(docstring)
- 推荐在头文件中包含copyright申明、模块描述
"""Licensed Materials -Property of CorpA (C) Copyright A corp.1999,2016 All Rights Reversed---------------------------------------------------------------File Name :Description :Author :---------------------------------------------------------------"""
适当添加空行的使代码布局更优雅、合理
基本遵循:
1)在一组代码表达完一个完整的思路之后,应该用空白进行间隔。
2)尽量保持上下文语义的易理解性。(如调用者放在上面,被调用者放在下面)
3)避免过长的代码行
4)不要为了保持水平的人使用多余的空格
5)空格的使用要能够在需要强调的时候警示读者
if x == 4: print x,y; x,y = y,x
编写函数4原则:
- 函数设计要劲量短小(避免过长函数),嵌套层次不宜过深(不超过三层)
- 函数申明应该做到合理简单易于使用,参数个数不宜过多。
- 参数设计应该考虑向下兼容(如
def readfile(filename,logger=logger.info):
) - 一个函数只做一件事,保持函数语句粒度的一致性,低耦合性。
将常量集中到一个文件
这里切换到Effective Python 59个有效方法:
建议二:PEP 8风格指南
使用space(空格)来表示缩进进,而不要用 tab(制表符).
和语法相关的每一层缩进都用 4 个空格来表示
或者用pycharm
每行字符串不超过79
文件中的函数与类之间用两个空行隔开
同一个类中,各方法之间应该用一个空行隔开。
在使用下标获取列表元素、调用函数或给出关键字参数赋值的时候,不要在两旁加space
为变量赋值的时候,赋值符号左侧和右侧各自写上一个空格。
命名:
函数、变量及属性应该用小写字母来拼写,各单词之间以下划线相连(hello_world)
受保护的实例属性,如_name
私有的实例属性,如__name
类与异常,应该全部采用大写字母,如RP_ERROR
类中的实例方法,应该把首个参数命名为self,以表示该对象自身。
类方法的首个参数,应该命名为cls,以表示自身。
表达式与语句
采用内联式的否定词,而不要把否定词放在整个表达式的前面
if a is not b
而不是(你的离散)if not a is b
不要通过检测长度的方法(if len(onelist)==0)来判断somelist是否为[]或“”等空值,而是应该采用
if not onelist:
来判断(同理if onelist:
)不要编写单行的if语句、for/while/except语句,而是应该把这些语句分成多行,装逼过多必自毙。
import语句应该总是放在文件开头
引入模块,总是应该使用绝对名称,而不应该根据当前模块的路径来使用想对名称
from bar import foo
而不是import foo
如果一定要以相对名称来编写import,就
from.import foo
文件中的那些import语句应该按顺序分为三个部分,各个import语句应该按模块的字母顺序来排序。
<完了>
第三条:了解bytes、str、unicode的区别
bytes包含原生8位值;str包含Unicode字符。
把Unicode字符表示为二进制数据可以采用UTF-8编码格式(记住必须使用encode和decode方法)
一定要把编码和解码放在界面最外围来做
附上原书的两个辅助函数:
def to_str(bytes_or_str): if isinstance(bytes_or_str,bytes): value = bytes_or_str.decode('utf-8') else: value = bytes_or_str return value # Istance of str
def to_bytes(bytes_or_str): if isinstance(bytes_or_str,str): value = bytes_or_str.encode('utf-8') else: value = bytes_or_str return value # Istance of bytes
python3中可能出现的问题:如果通过内置的open函数获取了文件句柄,会默认采用UTF-8编码格式。与python2相反。
不能以>或+等操作符来混同操作bytes和str实例
读取二进制数据,或写入二进制数据时,总应该以‘rb’’wb’等模式来开启文件
第四条:用辅助函数来取代复杂的表达式
附上书上的代码。。。
from urllib.parse import parse_qsmy_values = parse_qs('red=5&blue=0&green=', keep_blank_values=True)print(repr(my_values))
用get方法(接着上面):
print('Red: ',my_values.get('red'))print('Opacity ',my_values.get('opacity'))
下一个返回的是None
red = my_values.get('red',[''])[0] or 0green = my_values.get('green',[''])[0] or 0Opacity = my_values.get('Opacity',[''])[0] or 0print('Red: {}'.format(red))print('Greed: {}'.format(green))print('Opacity {}'.format(opacity))
这样的代码很难阅读。。。。。
red = int(my_values.get('red',[''])[0] or 0)
这样也是。。。。。
so red = my_values.get('red',[''])
red = int(red[0]) if red[0] else 0
这样的三元操作符。
如果需要频繁操作的话:
def get_first_int(values,key,default=0): found = values.get(key,['']) if found[0]: found = int(found[0]) else: found = default return found
so green = get_first_int(my_values,'green')
这样就更加清晰了
要点:误过度运用python语法特性;把复杂表达式移入辅助函数,反复使用相同逻辑;使用if/else优于or/and
第五条:了解切割序列的办法
基本写法是somelist[start:end],start包括在内end元素不包括在内。
a = ['a','b','c','d','e']print('First_Four:',a[:4])print('Last_Four:',a[-4:])print('Middle_One:'a[2:-2])#下面是断言assert a[:5] == a[0:5]assert a[5:] == a[5:len(a)]##越界也没问题###单个元素时,下标不能越界,否则IndexErrora[2:4]=[1]b = aassert b == a and b is ac=a[:]assert c == a and c is not a#浅拷贝与深拷贝
要点:不要多余的代码,start索引为0,或end索引为序列长度应省略
切片操作不计较是否越界;对list赋值,切片操作可以替换,即使场不同也可以替换。
第六条:在单次切片操作内,不要同时指定start、end、stride
除了基本切片操作,还有onelist[start:end:stride]
#反转onelist = [i for i in range(10)]onelist = onelist[::-1]
这种技巧对字节串和ASCII字符有用,但是对已经编码成UTF-8字节串的Unicode字符串来说,则无法奏效。
w = "你好"x = w.encode('utf-8')y = x[::-1]z = y.decode('utf-8')
自己尝试把。
itertools模块中也有一个islide方法,但这个方法不允许为start、end、stride指定负值。
要点:既有start、end、stride的切割操作,可能会令人费解;尽量使用stride为正数,且不带start或end索引的切割操作。尽量避免用负数做stride;不要同时使用start、end和stride。如果需要则拆解。
第七条:用列表来取代map和filter
squares = [x**2 for x in range(1,11) if x % 2 == 0]another_squares = list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, a)))#你可以对比一下。。。。。。
字典与集合也有类似的推导机制。
##附上书上的代码chile_ranks = {'ghost': 1, 'habanero': 2, 'cayenne': 3}rank_dict = {rank: name for name, rank in chile_ranks.items()}chile_len_set = {len(name) for name in rank_dict.values()}print(rank_dict)print(chile_len_set)
要点:列表推导要比map和filter函数清晰;列表推导可以跳过输入列表中的某些元素,如果改用map来做,那就必须辅以filter才能实现。
第八条:不要使用含有两个以上表达式的列表推导
列表推导支持多重循环。
matrix = [[1,2,3], [4,5,6], [7,8,9]]flat_matrix = [x for row in matrix for x in row]#当然也可以这样(建议)flat_matrix = []for row in matrix: flat_matrix.extend(row)
当然也有多重if。。。。。
matrix = [[1,2,3], [4,5,6], [7,8,9]]flat_matrix = [x for row in matrix if sum(row) >= 10 for x in row if x % 3 == 0]
不是太建议这样。。。
要点:列表推导支持多级循环,每一级循环也支持多项条件;抄过两个表达式的列表推导应尽量避免。
第九条:用生成器表达式来改写数据量较大的列表推导
假设你要打开一个很大的全新的列表,这时会消耗大量内存而导致程序崩溃。你读取一份文件并返回每行的字符数,若采用列表推导来做,则需要把文件的每一行的长度都保存在内存中,文件特别大时,无休止的network socket来读取。很容易产生问题。
#不推荐fo = open('C:\\Users\\Desktop\\Biglist.txt','r+')value = [len(x) for x in fo]#推荐it = (len(x) for x in fo)print(next(it))
要点:当输入的数据量较大时,列表推导可能会占用太多内存;有生成器表达式所返回的迭代器,可以逐次产生输入值,避免上述问题;串在一起的生成器表达式执行速度很快
第十条:尽量用enumerate 取代 range
虽然range函数很有用。
import randomrandom_bits = 0for i in range(64): if random.randint(0,1): random_bits |= 1 << iflavor_list = ['vanilla', 'chocolate', 'pecan','strawberry']for flavor in flavor_list: print('{} is delicious'.format(flavor))#原来这样 for i in range(len(flavor_list)): flavor = flavor_list[i] print('{}: {}'.format(i + 1, flavor))#现在推荐 for i, flavor in enumerate(flavor_list): print("{} : {}".format(i + 1, flavor))#也可以改成这样for i, flavor in enumerate(flavor_list,1): print("{} : {}".format(i, flavor))
要点:enumerate函数提供了一种精简的写法,可以在便利迭代器时获得索引。可以给enumerate提供第二个参数,替代range与小标访问相结合的序列。
第十一条:用zip函数同时遍历两个迭代器
在编写python代码,通常要面对很多列表,而这些列表里的对象,可能是相互关联。
names = ['Cecilia', 'Lise', 'Marie']letters =[len(n) for n in names]longest_name = Nonemax_letters = 0for i in range(len(names)): count = letters[i] if count > max_letters: longest_name = names[i] max_letters = countprint(longest_name) #用enumerate改正:for i, name in enumerate(names): count = letters[i] if count > max_letters: longest_name = name max_letters = count#用zip简化:for name, count in zip(names, letters): if count > max_letters: longest_name = name max_letters = count
Q1:Python2中并不是生成器,请用itertools中的izip函数
Q2:输入的迭代器长度不同,问题如下
names.append('huchi')for name, count in zip(names, letters): print(name)
只要耗尽一个迭代器就停止了,若不能确定zip所封装的列表是否等长。可以用itertools中的zip_longest函数(python2:izip_longest).
要点:zip可以平行遍历多个迭代器;python3中的zip相当于生成器。迭代器长度不等,zip会自动提前终止;itertools中zip_longest函数可以平行遍历多个迭代器,不用在乎他们长度是否相等。
第十二条:不要再for 和while循环后面写else块
#我很反对下面的代码,但还是敲出来了for i in range(3): print('Loop {}'.format(i))else: print('Else block!')for i in range(3): print('Loop {}'.format(i)) if i == 1:breakelse: print('Else block!')for x in[]: print('Never runs')else: print('For Else block!')#while同上 #对比以下代码:a,b = 4,9for i in range(2, min(a, b)+1): print('Testing', i) if a % i == 0 and b % i == 0 print('Not coprime') breakelse: print('Coprime')#anotherdef coprime(a, b): for i in range(2, min(a, b) + 1): if a % i == 0 and b % i == 0: return False return True #Last onedef coprime2(a, b): is_coprime = True for i in range(2, min(a, b) + 1): if a % i == 0 and b % i == 0: is_coprime = False break return is_coprime #循环容易理解
要点:不要用
第十三条:合理利用try/except/else/finally结构中的每个代码块
1.finally:如果要将异常向上传播,又要在异常发生时执行清理工作。
handle = open('hello.txt','r') #IOErrortry: data = handle .read() #UnicodeDecodeErrorfinally: handle.close() #read出错,异常传播到上面即引发IOError
2.else块:try/except/else结构可以描述哪些异常邮资机代码处理,哪些异常传播到上级。如果try没有异常,执行else块。
#从字符串中加载JSON字典数据,然后返回字典里某个键所对应的值import jsondef load_json_key(data,key): try: result_dict = json.load(data) #ValueError except ValueError as e: raise KeyError from e else: return result_dict[key] #KeyError#如果不是JSON格式,会产生ValueError#如果可以,else中的语句就会执行,若查询有异,则异常会向上传播
3.混合使用:try/except/else/finally结构。
#附上书上的代码从文件中读取某项事务的描述信息,处理及更新import jsonUNDEFINED = object()def divide_json(path): """ try读取文件并处理其内容 except来对应try可能发生的相关的异常 else块来实时更新文件并把更新中可能出现的异常回报给上级 finally来清理文件句柄 """ handle = open(path, 'r+') #IOError try: data = handle.read() #UnicodeDecodeError op = json.loads(data) #ValueError value = ( op['numerator']/ op['denominator']) #ZeroDivisionError except ZeroDivisionError as e: return UNDEFINED else: op['result'] = value result = json.dumps(op) handle.seek(0) handle.write(result) #IOError return value finally: handle.close()
要点:无论try块是否发生异常,都可以利用try/finally符合语句中的finally来执行清理工作;else可以用来所见try块的代码量,并把没有发生异常时所要执行的语句与try/except代码块隔开;顺利运行try块后,若想使某些操作能在finally块的清理代码之前执行,可写在else。
- 编写高质量的python(第一章)
- 如何编写高质量的Python程序
- 蛙蛙推荐:如何编写高质量的python程序
- 关于编写高质量的Python代码有感
- 用 Python 编写干净、可测试、高质量的代码
- 用Python编写干净 可测试 高质量的代码
- 用Python编写干净 可测试 高质量的代码
- 《编写高质量iOS与OS X代码的52个有效方法》之第一章要点
- 编写高质量的代码
- 编写高质量的程序
- 编写高质量的Makefile
- 编写高质量的Makefile
- 编写高质量的Makefile
- 编写高质量的Makefile
- 编写高质量的代码
- 编写高质量的JavaScript
- 读书笔记《编写高质量代码》高质量的HTML
- Effective Python:编写高质量Python代码的59个有效方法的学习笔记
- 谈谈HSTS超级Cookie
- 素数筛法
- 面向对象
- 0202.EBS-DBA0102.系统客户化开发初始化安装总结
- Eclipse离线安装Hibernate时进度条不动的问题。
- 编写高质量的python(第一章)
- rethinkdb安装
- CentOS──xxx is not in the sudoers file解决方法
- ftikftuki
- 大神级别的SEO,原来是这样炼成的!
- 响应式设计概述
- bower 安装bootstrap 提示installing Bootstrap with Bower on mac says: Error: EACCES, permission denied
- Eclipse中启动Tomcat服务器时输出日志位置修改
- rethinkdb命令