本系列文章为《编写高质量代码——改善Python程序的91个建议》的精炼汇总。
assert语句的基本语法以下:python
assert expression1 ["," expression2]
git
其中,expression1
是判断语句,会返回True或False,当返回False时会引起AssertionError。[]
中的内容表示是可选的,用来传递具体的异常信息。github
>>> a = 1 >>> b = 2 >>> assert a == b, "a equals b" Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError: a equals b
利用assert语句来发现程序中的问题。断言(assert)在不少语言中都存在,主要为调试程序服务,可以快速方便检查程序的异常或不恰当的输入。express
要注意的是使用assert是有代价的,它会对性能产生必定的影响,能够不用尽可能不用。安全
变量进行数据交换值时,不推荐使用中间变量。网络
# 交换x,y # 使用中间变量 temp = x x = y y = temp # 不使用中间变量 x, y = y, x
第二种方法在内存中执行的顺序以下:数据结构
Lazy evaluation 常被译为“延迟计算”或“惰性计算”,指的是仅仅在真正须要执行的时候才计算表达式的值。函数
def fib(): a, b = 0, 1 while True: yield a a, b = b, a + b from itertools import islice print(list(islice(fib(), 5)))
内建函数 type(object) 用于返回当前对象的类型。能够经过与 Python 自带模块 types 中所定义的名称进行比较,根据其返回值肯定变量类型是否符合要求。性能
全部基本类型对应的名称均可以在 types 模块中找到,然而使用 type() 函数并不适合用来进行变量类型检查。这是由于:ui
解决方法是,若是类型有对应的工厂函数,可使用工厂函数对类型作相应转换,不然可使用 isinstance() 函数来检测。
isinstance(object, classinfo)
其中,classinfo 能够为直接或间接类名、基本类型名称或者由它们组成的元组,该函数在 classinfo 参数错误的状况下会抛出 TypeError 异常。
# isinstance 基本用法举例以下: >>> isinstance(2, float) False >>> isinstance("a", (str, unicode)) True >>> isinstance((2, 3), (str, list, tuple)) # 支持多种类型列表 True
Python中eval()
函数将字符串当成有效的表达式来求值并返回计算结果。其函数声明以下:
eval(expression[, globals[, locals]])
其中,参数 globals 为字典形式,locals 为任何映射对象,它们分别表示全局和局部命名空间。若是传入 globals 参数的字典中缺乏 builtins 的时候,当前的全局命名空间将做为 globals 参数输入而且在表达式计算以前被解析。locals 参数默认与 globals 相同,若是二者都省略的话,表达式将在 eval() 调用的环境中执行。
eval 存在安全漏洞,一个简单的例子:
import sys from math import * def ExpCalcBot(string): try: print "Your answer is", eval(user_func) # 计算输入的值 except NameError: print "The expression you enter is not valid" print 'Hi, I am ExpCalcBot. please input your expression or enter e to end' inputstr = '' while True: print 'Please enter a number or operation. Enter c to complete. :' inputstr = raw_input() if inputstr == str('e'): # 遇到输入为 e 的时候退出 sys.exit() elif repr(inputstr) != repr(''): ExpCalcBot(inputstr) inputstr = ''
因为网络环境下运行它的用户并不是都是可信任的,好比输入 __import__("os").system("dir")
,会显示当前目录下的全部文件列表;若是恶意输入__import__("os").system("del * /Q")
,会致使当前目录下的全部文件都被删除了,而这一切没有任何提示。
在 globals 参数中禁止全局命名空间的访问:
def ExpCalcBot(string): try: math_fun_list = ["acos", "asin", "atan", "cos", "e", "log", "log10", "pi", "pow", "sin", "sqrt", "tan"] math_fun_dict = dict([(k, globals().get(k)) for k in math_fun_list]) # 造成能够访问的函数的字典 print "Your name is", eval(string, {"__builtins__": None}, math_fun_dict) except NameError: print "The expression you enter is not valid"
再次进行恶意输入:[c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == "Quitter"][0](0)()
,
# ().__class__.__bases__[0].__subclasses__()
用来显示 object 类的全部子类。类 Quitter 与 "quit" 功能绑定,所以上面的输入会致使程序退出。
对于有经验的侵入者来讲,他可能会有一系列强大的手段,使得 eval 能够解释和调用这些方法,带来更大的破坏。此外,eval() 函数也给程序的调试带来必定困难,要查看 eval() 里面表达式具体的执行过程很难。所以在实际应用过程当中若是使用对象不是信任源,应该避免使用 eval,在须要使用 eval 的地方可用安全性更好的ast.literal_eval
替代。
使用函数 enumerate(),主要是为了解决在循环中获取索引以及对应值的问题。它具备必定的惰性(lazy),每次只在须要的时候才会产生一个(index, item)对。函数签名以下:
enumerate(sequence, start=0)
例子:
# 使用 enumerate() 获取序列迭代的索引和值 li = ['a', 'b', 'c', 'd', 'e'] for i, e in enumerate(li): print("index:", i, "element:", e)
==
:用来检验两个对象的值是否相等的。它实际调用内部 __eq__()
方法,所以 a == b
至关于 a.__eq__(b)
。is
:用来比较两个对象在内存中是否拥有同一块内存空间。仅当 x 和 y 是同一个对象的时候才返回 True,x is b
基本至关于 id(x) == id(y)
。==
操做符也是能够被重载的,而 is
不能被重载。通常状况下,若是 x is y
为 True , x == y
的值通常也为 True(特殊状况除外,如 NaN
,a = float('NaN')
,a is a
为 True,a == a
为 false)。
每个 Python 文件均可以当作一个模块(module),使用模块能够加强代码的可维护性和可重用性。
包便是目录,但与普通目录不一样,它除了包含常规的 Python 文件(也就是模块)之外,还包含一个 __init__.py
文件,同时它容许嵌套。
Package/__init__.py Module1.py Module2.py Subpackage/__init__.py Module1.py Module2.py
包中的模块能够经过"."访问符进行访问,即"包名.模块名"。有如下几种导入方法:
import Package
导入子模块或子包,包嵌套的状况下能够进行嵌套导入:
from Package import Module1 import Package.Module1 from Package import Subpackage import Package.Subpackage from Package.Subpackage import Module1 import Package.Subpackage.Module1
__init__.py
的做用:
若是 __init__.py
文件为空,当意图使用 from Package import *
将包 Package 中全部的模块导入当前名字空间时,并不能使得导入的模块生效,这是由于不一样平台间的文件的命名规则不一样,Python 解释器并不能正确断定模块在对应的平台该如何导入,所以仅仅执行 __init__.py
文件,若是要控制模块的导入,则须要对 __init__.py
文件作修改。
__init__.py
文件还有一个做用就是经过在该文件中定义 __all__
变量,控制须要导入的子包或者模块。以后再运行 from ... import *
,能够看到 __all__
变量中定义的模块和包被导入当前名字空间。
包的使用可以带来如下便利:
若是模块包含的属性和方法存在同名冲突,使用 import module
能够有效地避免名称冲突。在嵌套的包结构中,每个模块都以其所在的完整路径做为其前缀,所以,即便名称同样,但因为模块所对应的其前缀不一样,就不会产生冲突。
本篇文章由一文多发平台ArtiPub自动发布