建议 24:遵循异常处理的几点基本原则
异常处理的几点原则:
- 注意异常的粒度,不推荐在 try 中放入过多的代码
- 谨慎使用单独的 except 语句处理所有异常,最好能定位具体的异常
- 注意异常捕获的顺序,在适合的层次处理异常,Python 是按内建异常类的继承结构处理异常的,所以推荐的做法是将继承结构中子类异常在前抛出,父类异常在后抛出
- 使用更为友好的异常信息,遵守异常参数的规范
建议 25:避免 finally 中可能发生的陷阱
当 finally 执行完毕时,之前临时保存的异常将会再次被抛出,但如果 finally 语句中产生了新的异常或执行了 return 或 break 语句,那么临时保存的异常将会被丢失,从而异常被屏蔽。
在实际开发中不推荐 finally 中使用 return 语句进行返回。
建议 26:深入理解 None,正确判断对象是否为空
类型FalseTrue布尔False (与0等价)True (与1等价)字符串””( 空字符串)非空字符串,例如 ” “, “blog”数值0, 0.0非0的数值,例如:1, 0.1, -1, 2容器[], (), {}, set()至少有一个元素的容器对象,例如:[0], (None,), [”]NoneNone非None对象
<span class="hljs-meta">>>> </span>id(<span class="hljs-keyword">None</span>) <span class="hljs-number">10743840</span> <span class="hljs-meta">>>> </span>a = <span class="hljs-keyword">None</span> <span class="hljs-meta">>>> </span>id(a) <span class="hljs-number">10743840</span> <span class="hljs-meta">>>> </span>l = [] <span class="hljs-meta">>>> </span><span class="hljs-keyword">if</span> l <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">None</span>: <span class="hljs-comment"># 判断逻辑 l 不为空</span> <span class="hljs-meta">... </span> print(<span class="hljs-string">'l is {}'</span>.format(l)) <span class="hljs-meta">... </span><span class="hljs-keyword">else</span>: <span class="hljs-meta">... </span> print(<span class="hljs-string">'l is empty'</span>) <span class="hljs-meta">... </span> l <span class="hljs-keyword">is</span> [] <span class="hljs-meta">>>> </span><span class="hljs-keyword">if</span> l: <span class="hljs-comment"># #3 正确的判断形式</span> <span class="hljs-meta">... </span> print(<span class="hljs-string">'Do something...'</span>) <span class="hljs-meta">... </span><span class="hljs-keyword">else</span>: <span class="hljs-meta">... </span> print(<span class="hljs-string">'Do other thing...'</span>) <span class="hljs-meta">... </span> Do other thing...
#3执行中会调用__nonzero__()来判断自身对象是否为空并返回0/1或True/False,如果没有定义该方法,Python 将调用__len__()进行判断,返回 0 表示为空。如果一个类既没有定义__len__()又没有定义__nonzero__(),该类实例用 if 判断为True。
建议 27:连接字符串优先使用 join 而不是 +
这一点之前我在博文里总结过,+涉及到更多的内存操作。
建议 28:格式化字符串时尽量使用 .format 而不是 %
同上。
建议 29:区别对待可变对象和不可变对象
Python 中一切皆对象,每个对象都有一个唯一的标识符(id)、类型(type)和值。数字、字符串、元组属于不可变对象,字典、列表、字节数组属于可变对象。
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Student</span>(<span class="hljs-title">object</span>):</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(<span class="hljs-keyword">self</span>, name, course=[])</span></span>: <span class="hljs-comment"># 问题就出在这里</span> <span class="hljs-keyword">self</span>.name = name <span class="hljs-keyword">self</span>.course = course <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">addcourse</span><span class="hljs-params">(<span class="hljs-keyword">self</span>, coursename)</span></span>: <span class="hljs-keyword">self</span>.course.append(coursename) <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">printcourse</span><span class="hljs-params">(<span class="hljs-keyword">self</span>)</span></span>: <span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> <span class="hljs-keyword">self</span>.<span class="hljs-symbol">course:</span> print(item) stuA = Student(<span class="hljs-string">'Wang yi'</span>) stuA.addcourse(<span class="hljs-string">'English'</span>) stuA.addcourse(<span class="hljs-string">'Math'</span>) print(<span class="hljs-string">"{}'s course: "</span>.format(stuA.name)) stuA.printcourse() print(<span class="hljs-string">'---------------------------'</span>) stuB = Student(<span class="hljs-string">'Su san'</span>) stuB.addcourse(<span class="hljs-string">'Chinese'</span>) stuB.addcourse(<span class="hljs-string">'Physics'</span>) print(<span class="hljs-string">"{}'s course: "</span>.format(stuB.name)) stuB.printcourse() <span class="hljs-comment"># run</span> Wang yi<span class="hljs-string">'s course: English Math --------------------------- Su san'</span>s <span class="hljs-symbol">course:</span> English Math Chinese Physics
默认参数在初始化时仅仅被评估一次,以后直接使用第一次评估的结果,course 指向的是 list 的地址,每次操作的实际上是 list 所指向的具体列表,所以对于可变对象的更改会直接影响原对象。
最好的方法是传入None作为默认参数,在创建对象的时候动态生成列表。
<span class="hljs-meta">>></span>> list1 = [<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>] <span class="hljs-meta">>></span>> list2 = list1 <span class="hljs-meta">>></span>> list1.append(<span class="hljs-string">'d'</span>) <span class="hljs-meta">>></span>> list2 [<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>, <span class="hljs-string">'d'</span>] <span class="hljs-meta">>></span>> list3 = list1[<span class="hljs-symbol">:</span>] <span class="hljs-comment"># 可变对象的切片操作相当于浅拷贝</span> <span class="hljs-meta">>></span>> list3.remove(<span class="hljs-string">'a'</span>) <span class="hljs-meta">>></span>> list3 [<span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>, <span class="hljs-string">'d'</span>] <span class="hljs-meta">>></span>> list1 [<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>, <span class="hljs-string">'c'</span>, <span class="hljs-string">'d'</span>]
建议 30:[]、() 和 {} 一致的容器初始化形式
<span class="hljs-meta">>>> </span>list1 = [[<span class="hljs-string">'Hello'</span>, <span class="hljs-string">'World'</span>], [<span class="hljs-string">'Goodbye'</span>, <span class="hljs-string">'World'</span>]] <span class="hljs-meta">>>> </span>list2 = [[s.upper() <span class="hljs-keyword">for</span> s <span class="hljs-keyword">in</span> xs] <span class="hljs-keyword">for</span> xs <span class="hljs-keyword">in</span> list1] <span class="hljs-meta">>>> </span>list2 [[<span class="hljs-string">'HELLO'</span>, <span class="hljs-string">'WORLD'</span>], [<span class="hljs-string">'GOODBYE'</span>, <span class="hljs-string">'WORLD'</span>]] <span class="hljs-meta">>>> </span>[v**<span class="hljs-number">2</span> <span class="hljs-keyword">if</span> v%<span class="hljs-number">2</span> == <span class="hljs-number">0</span> <span class="hljs-keyword">else</span> v+<span class="hljs-number">1</span> <span class="hljs-keyword">for</span> v <span class="hljs-keyword">in</span> [<span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">-1</span>] <span class="hljs-keyword">if</span> v><span class="hljs-number">0</span>] [<span class="hljs-number">4</span>, <span class="hljs-number">4</span>, <span class="hljs-number">16</span>]
其实就是列表生成式、元组生成式和字典生成式。
建议 31:记住函数传参既不是传值也不是传引用
正确的说法是传对象(call by object)或传对象的引用(call-by-object-reference),函数参数在传递过程中将整个对象传入,对可变对象的修改在函数外部以及内部都可见,对不可变对象的”修改“往往是通过生成一个新对象然是赋值实现的。
建议 32:警惕默认参数潜在的问题
其中就是默认参数如果是可变对象,在调用者和被调用者之间是共享的。
<span class="hljs-keyword">import</span> time <span class="hljs-comment"># 对当前系统时间进行处理</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">report</span><span class="hljs-params">(when=time.time)</span>:</span> <span class="hljs-comment"># 而不是when=time.time()</span> <span class="hljs-keyword">pass</span>
建议 33:慎用变长参数
原因如下:
- 使用过于灵活,导致函数签名不够清晰,存在多种调用方式
- 使用*args和**kw简化函数定义就意味着函数可以有更好的实现方法
使用场景:
- 为函数添加一个装饰器
- 参数数目不确定
- 实现函数的多态或子类需要调用父类的某些方法时
建议 34:深入理解 str() 和repr() 的区别
总结几点:
- str()面向用户,返回用户友好和可读性强的字符串类型;repr()面向 Python 解释器或开发人员,返回 Python 解释器内部的含义
- 解释器中输入a默认调用repr(),而print(a)默认调用str()
- repr()返回值一般可以用eval()还原对象:obj == eval(repr(obj))
- 以上两个方法分别调用内建的__str__()和__repr__(),一般来说类中都应该定义__repr__(),但当可读性比准确性更为重要时应该考虑__str__(),用户实现__repr__()方法的时候最好保证其返回值可以用eval()是对象还原
建议 35:分清 staticmethod 和 classmethod 的适用场景
这两种方法之前已经总结过了的,下面我们只讨论它们的使用场景。
调用类方法装饰器的修饰器的方法,会隐式地传入该对象所对应的类,可以动态生成对应的类的类变量,同时如果我们期望根据不同的类型返回对应的类的实例,类方法才是正确的解决方案。
反观静态方法,当我们所定义的方法既不跟特定的实例相关也不跟特定的类相关,可以将其定义为静态方法,这样使我们的代码能够有效地组织起来,提高可维护性。
当然,也可以考虑定义一个模块,将一组的方法放入其中,通过模块来访问。
第 4 章 库
建议 36:掌握字符串的基本用法
<span class="hljs-comment"># 小技巧:Python 遇到未闭合的小括号会自动将多行代码拼接为一行</span> <span class="hljs-meta">>></span>> s = (<span class="hljs-string">'SELECT * '</span> ... <span class="hljs-string">'FROM table '</span> ... <span class="hljs-string">'WHERE field="value"'</span>) <span class="hljs-meta">>></span>> s <span class="hljs-string">'SELECT * FROM table WHERE field="value"'</span> <span class="hljs-comment"># Python2 中使用 basestring 正确判断一个变量是否是字符串</span> <span class="hljs-comment"># 性质判断</span> isalnum() isalpha() isdigit() islower() isupper() isspace() istitle() <span class="hljs-comment"># 查找替换</span> startswith(prefix[, start[, <span class="hljs-keyword">end</span>]]) endswith(suffix[, start[, <span class="hljs-keyword">end</span>]]) <span class="hljs-comment"># prefix参数可以接收 tuple 类型的实参</span> count(sub[, start[, <span class="hljs-keyword">end</span>]]) find(sub[, start[, <span class="hljs-keyword">end</span>]]) index(sub[, start[, <span class="hljs-keyword">end</span>]]) rfind(sub[, start[, <span class="hljs-keyword">end</span>]]) rindex(sub[, start[, <span class="hljs-keyword">end</span>]]) replace(old, new[, count]) <span class="hljs-comment"># count是指的替换次数,不指定就全部替换</span> <span class="hljs-comment"># 切分</span> partition(sep) rpartition(sep) splitlines([keepends]) split([sep, [, maxsplit]]) rsplit([sep[, maxsplit]]) <span class="hljs-comment"># partition 返回一个3个元素的元组对象</span> <span class="hljs-comment"># 变形</span> lower() upper() capitalize() swapcase() title() <span class="hljs-comment"># 删减填充</span> strip([chars]) lstrip([chars]) rstrip([chars]) <span class="hljs-comment"># 没有提供chars默认是空白符,由string.whitespace 常量定义</span> center(width[, fillchar]) ljuct(width[, fillchar]) rjust(width[, fillchar]) zfill(width) expandtabs([tabszie])
下面来介绍一些易混淆的地方:
<span class="hljs-meta">>></span>> <span class="hljs-string">' hello world'</span>.split() [<span class="hljs-string">'hello'</span>, <span class="hljs-string">'world'</span>] <span class="hljs-meta">>></span>> <span class="hljs-string">' hello world'</span>.split(<span class="hljs-string">' '</span>) [<span class="hljs-string">''</span>, <span class="hljs-string">''</span>, <span class="hljs-string">'hello'</span>, <span class="hljs-string">'world'</span>] <span class="hljs-meta">>></span>> <span class="hljs-string">'hello wORld'</span>.title() <span class="hljs-string">'Hello World'</span> <span class="hljs-meta">>></span>> import string <span class="hljs-meta">>></span>> string.capwords(<span class="hljs-string">' hello world!'</span>) <span class="hljs-string">'Hello World!'</span> <span class="hljs-meta">>></span>> string.whitespace <span class="hljs-string">' \t\n\r\x0b\x0c'</span>
建议 37:按需选择 sort() 或者 sorted()
<span class="hljs-comment"># 函数原型</span> sorted(iterable[, cmp[, key[, reverse]]]) <span class="hljs-comment"># 返回一个排序后的列表</span> s.sort([cmp[, key[, reverse]]]) <span class="hljs-comment"># 直接修改原列表,返回为None</span> <span class="hljs-meta">>></span>> persons = [{<span class="hljs-string">'name'</span>: <span class="hljs-string">'Jon'</span>, <span class="hljs-string">'age'</span>: <span class="hljs-number">32</span>}, {<span class="hljs-string">'name'</span>: <span class="hljs-string">'Alan'</span>, <span class="hljs-string">'age'</span>: <span class="hljs-number">50</span>}, {<span class="hljs-string">'name'</span>: <span class="hljs-string">'Bob'</span>, <span class="hljs-string">'age'</span>: <span class="hljs-number">23</span>}] <span class="hljs-meta">>></span>> sorted(persons, key=lambda <span class="hljs-symbol">x:</span> (x[<span class="hljs-string">'name'</span>], -x[<span class="hljs-string">'age'</span>])) [{<span class="hljs-string">'name'</span>: <span class="hljs-string">'Alan'</span>, <span class="hljs-string">'age'</span>: <span class="hljs-number">50</span>}, {<span class="hljs-string">'name'</span>: <span class="hljs-string">'Bob'</span>, <span class="hljs-string">'age'</span>: <span class="hljs-number">23</span>}, {<span class="hljs-string">'name'</span>: <span class="hljs-string">'Jon'</span>, <span class="hljs-string">'age'</span>: <span class="hljs-number">32</span>}] <span class="hljs-meta">>></span>> a = (<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>) <span class="hljs-meta">>></span>> sorted(a) [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>]
所以如果实际过程中需要保留原有列表,可以使用sorted()。sort()不需要复制原有列表,消耗内存较小,效率较高。同时传入参数key比传入参数cmp效率要高,cmp传入的函数在整个排序过程中会调用多次,而key针对每个元素仅作一次处理。
建议 38:使用 copy 模块深拷贝对象
- 浅拷贝(shallow copy):构造一个新的复合对象并将从原对象中发现的引用插入该对象中。工厂函数、切片操作、copy 模块中的 copy 操作都是浅拷贝
- 深拷贝(deep copy):针对引用所指向的对象继续执行拷贝,因此产生的对象不受其它引用对象操作的影响。深拷贝需要依赖 copy 模块的 deepcopy() 操作
在 python 中,标识一个对象唯一身份的是:对象的id(内存地址),对象类型,对象值,而浅拷贝就是创建一个具有相同类型,相同值但不同id的新对象。因此使用浅拷贝的典型使用场景是:对象自身发生改变的同时需要保持对象中的值完全相同,比如 list 排序:
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">sorted_list</span><span class="hljs-params">(olist, key=None)</span>:</span> copied_list = copy.copy(olist) copied_list.sort(key=key) <span class="hljs-keyword">return</span> copied_list a = [<span class="hljs-number">3</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>] <span class="hljs-comment"># [3, 2, 1]</span> b = sorted_list(a) <span class="hljs-comment"># [1, 2, 3]</span>
深拷贝不仅仅拷贝了原始对象自身,也对其包含的值进行拷贝,它会递归的查找对象中包含的其他对象的引用,来完成更深层次拷贝。因此,深拷贝产生的副本可以随意修改而不需要担心会引起原始值的改变:
<span class="hljs-meta">>></span>> a = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>] <span class="hljs-meta">>></span>> b = [a, a] <span class="hljs-meta">>></span>> b [[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>], [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>]] <span class="hljs-meta">>></span>> from copy import deepcopy <span class="hljs-meta">>></span>> c = deepcopy(b) <span class="hljs-meta">>></span>> id(b[<span class="hljs-number">0</span>]) == id(c[<span class="hljs-number">0</span>]) False <span class="hljs-meta">>></span>> id(b[<span class="hljs-number">0</span>]) == id(b[<span class="hljs-number">1</span>]) True <span class="hljs-meta">>></span>> c [[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>], [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>]] <span class="hljs-meta">>></span>> c[<span class="hljs-number">0</span>].append(<span class="hljs-number">3</span>) <span class="hljs-meta">>></span>> c [[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>], [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]]
使用 _copy_ 和 __deepcopy__ 可以完成对一个对象拷贝的定制。
建议 39: 使用 Counter 进行计数统计
常见的计数统计可以使用dict、defaultdict、set和list,不过 Python 提供了一个更优雅的方式:
<span class="hljs-meta">>></span>> from collections import Counter <span class="hljs-meta">>></span>> some_data = {<span class="hljs-string">'a'</span>, <span class="hljs-string">'2'</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-string">'c'</span>, <span class="hljs-string">'7'</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-string">'d'</span>, <span class="hljs-string">'b'</span>} <span class="hljs-meta">>></span>> Counter(some_data) Counter({<span class="hljs-string">'7'</span>,: <span class="hljs-number">1</span>, <span class="hljs-number">2</span>: <span class="hljs-number">1</span>, <span class="hljs-number">3</span>: <span class="hljs-number">1</span>, <span class="hljs-number">4</span>: <span class="hljs-number">1</span>, <span class="hljs-number">5</span>: <span class="hljs-number">1</span>, <span class="hljs-string">'2'</span>: <span class="hljs-number">1</span>, <span class="hljs-string">'b'</span>: <span class="hljs-number">1</span>, <span class="hljs-string">'a'</span>: <span class="hljs-number">1</span>, <span class="hljs-string">'d'</span>: <span class="hljs-number">1</span>, <span class="hljs-string">'c'</span>: <span class="hljs-number">1</span>})
Counter 类属于字典类的子类,是一个容器对象,用来统计散列对象,支持+、-、&、|,其中&和|分别返回两个 Counter 对象各元素的最小值和最大值。
<span class="hljs-comment"># 初始化</span> Counter(<span class="hljs-string">'success'</span>) Counter(s=<span class="hljs-number">3</span>, c=<span class="hljs-number">2</span>, e=<span class="hljs-number">1</span>, u=<span class="hljs-number">1</span>) Counter({<span class="hljs-string">'s'</span>: <span class="hljs-number">3</span>, <span class="hljs-string">'c'</span>: <span class="hljs-number">2</span>, <span class="hljs-string">'u'</span>: <span class="hljs-number">1</span>, <span class="hljs-string">'e'</span>: <span class="hljs-number">1</span>}) <span class="hljs-comment"># 常用方法</span> <span class="hljs-keyword">list</span>(Counter(some_data).elements()) <span class="hljs-comment"># 获取 key 值</span> Counter(some_data).most_common(<span class="hljs-number">2</span>) <span class="hljs-comment"># 前 N 个出现频率最高的元素以及对应的次数</span> (Counter(some_data))[<span class="hljs-string">'y'</span>] <span class="hljs-comment"># 访问不存在的元素返回 0</span> c = Counter(<span class="hljs-string">'success'</span>) c.update(<span class="hljs-string">'successfully'</span>) <span class="hljs-comment"># 更新统计值</span> c.subtract(<span class="hljs-string">'successfully'</span>) <span class="hljs-comment"># 统计数相减,允许为0或为负</span>
建议 40:深入掌握 ConfigParser
几乎所有的应用程序都会读取配置文件,ini是一种比较常见的文件格式:
<span class="hljs-section">[section1]</span> <span class="hljs-attr">option1</span>=<span class="hljs-number">0</span>
Python 提供标准库 ConfigParser 来支持它:
<span class="hljs-keyword">import</span> ConfigParser conf = ConfigParser.ConfigParser() conf.read(<span class="hljs-string">'example.conf'</span>) print(conf.get(<span class="hljs-string">'section1'</span>, <span class="hljs-string">'in_default'</span>))
再来看个SQLAlchemy配置文件的例子:
<span class="hljs-section">[DEFAULT]</span> <span class="hljs-attr">conn_str</span> = %(dbn)s://%(user)s:%(pw)s@%(host)s:%(port)s/%(db)s <span class="hljs-attr">dbn</span> = mysql <span class="hljs-attr">user</span> = root <span class="hljs-attr">host</span> = localhost <span class="hljs-attr">port</span> = <span class="hljs-number">3306</span> <span class="hljs-section">[db1]</span> <span class="hljs-attr">user</span> = aaa <span class="hljs-attr">pw</span> = ppp <span class="hljs-attr">db</span> = example <span class="hljs-section">[db2]</span> <span class="hljs-attr">host</span> = <span class="hljs-number">192.168</span>.<span class="hljs-number">0.110</span> <span class="hljs-attr">pw</span> = www <span class="hljs-attr">db</span> = example
<span class="hljs-keyword">import</span> ConfigParser conf = ConfigParser.ConfigParser() conf.read(<span class="hljs-string">'format.conf'</span>) print(conf.get(<span class="hljs-string">'db1'</span>, <span class="hljs-string">'conn_str'</span>)) print(conf.get(<span class="hljs-string">'db2'</span>, <span class="hljs-string">'conn_str'</span>))
转载自http://www.codeceo.com/article/91-suggestions-about-python-part-two.html#0-qzone-1-78981-d020d2d2a4e8d1a374a433f596ad1440