1、延迟加载模块
延迟加载模块是指在全局导入时并不加载的模块。在Python中,import语句可以包含在函数内部,这样导入是在函数调用时才发生,而不是在全局导入时发生。在某些情况下,模块的这种加载方式可能比较合理,但是大多是情况下,这只是对设计不加的模块结构的变通方法(例如避免死循环导入),通常应避免这种加载方式,当然对于标准模块来说,没有理由使用延迟加载。
2、项目基于Semver的好处
通常情况下只有主版本才需要测试,因为次版本和修订版本中保证没有向后不兼容的变化。只有项目不违背这样的约定,这种说法才成立。由于semver声称对次版本和修订版本的变化保持严格的向后兼容,那么打破这个规则就可以视为BUG,可以在修订版本中进行修复
future语句功能是和语法相关的元素,其他方法很难处理这些元素。这个语句只能影响他所在的模块。下面列出所有可用的__future__语句:
- division: Python3新增的除法运算符(PEP238)
- absolute_import:将所有不以点字符开头的import语句格式解释为绝对导入(PEP328)
- print_function:将print语句变为函数调用,所以在print后面必须加括号(PEP3112)
- unicode_literals:将每个字符串解释为Unicode(PEP 3112)
3、 性能优化技巧
3.1字符串拼接
例如平常都是在用多个字符串的重复拼接操作来创建一个新的字符串:
s = ""
for substring in substrings:
s+=substring
这样会导致运行时间成本与字符串总长度成二次函数关系。换句话说,这种方法效率极低。处理这种问题可以使用str.join()方法,他接受可迭代的字符串作为参数,返回合并后的字符串。由于这是一个方法,实际的做法是利用空字符串来调用它:
s = "".join(substrings)
字符串的这一方法还可以用于在需要合并的多个子串之间插入分隔符
','.join(['some','comma','separated','values'])
输出:'some,comma.separated,values'
但是并不意味着所有情况都能用,以下例子不适合使用join:
- 如果子字符串的数量很少,而且已经包含在某个可迭代对象中,那么在某些情况下,创建一个新序列来进行拼接操作的开销可能会超过使用join()节省下来的开销
- 在拼接短的字面值,由于CPython中的常数折叠(constant folding),一些复杂的字面值(不只是字符串)在编译时会转换成更短的形式,例如’a’+‘b’+‘c’被转换成’abc’,当然,这只使用短的常量
3.2常数折叠和窥孔优化程序
CPython对于编译过的源代码使用窥孔优化城乡来提高其性能,这种优化程序直接对Python字节码实现许多常见的优化,。
常数折叠是其功能之一
生成常数的长度不能超过一个固定值。在python3.5中这个固定值仍然是20。
4、Python列表实现原理
Python中的列表是由其对它对象的引用组成的连续数组,指向这个数组的指针及其长度被保存在一个列表头结构中。这意味着,每次添加或者删除一个元素是,由引用组成的数组改变大小(重新分配)。Python在创建这些数组的时候采用了指数过分配,所以并不是每一次操作都是需要改变数组大小。这也是添加或者取出元素的平坦复杂度较低的原因。不过,在普通链表中“代价很小”的其他一些操作在Python中的计算复杂度缺相对较高。
- 利用list.insert方法在任意位置插入一个元素-----复杂度为O(n)
- 利用list.delete或者del删除一个元素----复杂度为O(n)
5、列表推导式(一般优化for循环)
evens = []
for i in range(10):
if i %2 ==0:
evens.append(i)
这种写法可能适合C语言,但是在Python中运行速度很慢
- 解释器在每一次循环中都需要判断序列中的那一部分需要修改
- 需要用一个计数器来跟踪需要处理的元素
- 由于append是一个列表方法,所以每次遍历还要额外执行一个查询函数
[i for i in range(10) if i%2==0]
列表推导式是使用编排好的功能对上述雨大的一部分进行自动优化
5.1 列表推导和内部数组调整大小
解释器在对列表推导式进行求值的过程中并不知道最终结果容器的大小,也就是无法为他预先分配数组的最终大小。因此,内部数组的重新分配方式与for循环中完全相同,但是大多数情况下,与普通循环相比,使用列表推导式更加简洁和快速。
6、字典实现原理
CPython中使用伪随机探测的散列表作为字典底层数据结构。只有可哈希对象才能作为字典的键。如果一个对象有一个在整个生命周期都不变的散列值,而且这个值可以与其他对象进行比较,那么这个对象就是可哈希
Python中所有不可变的内置类型都是可哈希的。可变(列表,字典和集合)是不可哈希的,因此不能作为字典的键。可哈希类型协议包括下面两个方法:
- hash:这以防范给出dict内部实现需要的散列值(整数)。对于用户自定义的实例对象,这个值由id()给出
- eg:比较两个对象的值是否相等,对于用户自定义类,除了自身之外,所有的实例对象默认不相等
7、集合
集合是一个鲁棒性很好的数据结构,当元素顺序的重要性不如元素的唯一性和测试元素是否包含在集合中的效率是,大部分情况下这种数据结构是很有用的
实现细节:
集合被实现为带有空值的字典,只有键才是实际的集合元素。此外,集合还利用这种没有值的映射做了其他优化。
8、XML-RPC协议
是一种轻量级的远程过程调用协议,通过HTTP使用XML对调用进行编码。对于简单的客户-服务端器交换,通常使用这各种协议而不是SOAP。SOAP提供了列出所有可调函数的页面(WSDL),XML-RPC与之不同,并没有可调用函数的目录
9、上下文管理器With
为了确保即使出现错误的情况下也能运行某些清理代码,try…finally语句是很有用的。以下是使用场景:
- 关闭一个文件
- 释放一个锁
- 创建一个临时代码补丁
with语句为了这些使用场景的代码块包装一个简单的方法,即使改代码引发了异常,你也可以在其执行前后调用一些代码。例如处理文件通常采用这种方式:
hosts = open('/etc/hosts')
try:
for line in hosts:
if line.startswith("#'):
continue
print(line.strip())
finally:
hosts.close()
利用with可以重写
with open('etc/hosts/') as hosts:
for line in hosts:
if line.startswith('#'):
continue
print(line.strip())
open的作用使上下文管理器,确保即使出现异常也要在执行完for循环之后关闭文件
与这条语句兼容的其他项目来自Threading模块的类:
- threading.Lock
- threading.RLock
- threading.Condition
- threading.Semaphore
- threading.BoundedSemaphore
此外,如果上下文管理提供上下文变量,可用as子句保存为局部变量
with context_manager as context:
#代码块
....
注意,多个上下文管理可以同时使用
with A() as a, B() as b:
....
9.1 作为一个类
任何实现了上下文管理器协议的对象都可以作用于上下文管理器。该协议有两个特殊方法:
- enter(self)
- exit(self,exc_type,exc_value,traceback)
简而言之,执行with语句的过程如下: - __enter__方法。任何返回值都会绑定到指定的as子句
- 执行内部代码块
- 调用__exit__方法
9.2 作为一个函数----contextlib模块
标准库中新增了contextlib模块,提供了上下文管理器一起使用的辅助函数。他最有用的部分是contextmanager装饰器。你可以在一个函数里同时提供__enter__和__exit__两个部分,中间用yield语句分开
from context import contextmanager
@contextmanager
def context_illustration():
print('entering context')
try :
yield
except Exception as e:
print('leaving context')
print('with an error(%s)'%e)
#需要抛出异常
raise
else:
print('leaving context')
print('with no error')
context_illustration提供3个辅助函数:
- closing(element):返回一个上下文管理器,在退出时会调用钙元素的close方法。例如,他对处理流的类就很有用
- supress(*exceptions):他会压制发生在with语句正文中的特定异常。
- redirect_stdout(new_target)和redirect_stderr(new_target_:它会将代码内任何代码的sys.stdout或sys.stderr输出重定向到类文件file-like对象的灵异文件。
10、exec,eval和compile
- exec(object,global,locals):这一函数允许你动态执行python代码。object应该一个字符串或代码对象。global和locals参数为所执行的代码提供全局的和局部的命名空间。global必须是字典,而local可以使任何映射对象。其返回值始终未None
- eval(expression,globals,locals):这一函数用于对给定代表式进行求值并返回结果。它与exec()类似,但是接受的expression应该是单一python表达式,而不是一系列语句。他返回表达式求值的结果
- compile(source,filename,mode):这一函数将源代码编译成代码对象或AST对象。要编译的代码在source参数中作为字符串提供。filename应该是读取代码的文件。
11、list截取
如果要截取最后一个或者最后几位,建议使用str[0:-1],str[0:-2]
str[:len(str)-1]这种有点多余
12、常见算法优化例子
- 1、list
count = 10**5
nums = []
for i in range(count):
nums.append(i)
nums.reverse()
精简化:
nums = []
for i in range(count):
nums.insert(0,i)
感觉这段代码比上一段代码更简洁,很不错,但是如果你去试试执行速度感觉上一个会比较快。因为第二段代码完成任务所需要的时间是第一段的200倍
13、隐性平方级操作
lists=[[1,2],[3,4,5],[6]]
sum(lists,[])
sum函数是平方级的运行时间
优化
res = []
for i in lists:
res.extend(i)
14、不要对浮点数进行等值比较
可以检查他们是否接近等于例如
def almost_equal(x,y,places=7):
return round(abs(x-y),places)==0
almost_equal(sum(0.1 for i in range(10)),1.0)
True
15、内存泄漏
list = [1,2,3,4,…]
假如有一千万条数据,使用完成之后,直接list = []。以为这样做就能清空上一条数据,其实list指向了一个新的地址,一千万条数据的list依然存在在内存中。尽管python有垃圾回收机制但是如果程序很大,不一定能够做到及时回收操作,因此一定程度上会造成内存泄漏。
正确的做法是
list.clear()或者del list