python高手之路 笔记4 性能与优化专题

性能与优化

数据结构

多利用python本身的代码

  • dict.get()示例
#dict.get(key, default=None)
#key -- 这是要搜索在字典中的键。
#default -- 这是要返回键不存在的的情况下默认值。
def get_fruits(basket, fruit):
    return basket.get(fruit, set())
  • set()示例
#集合做差集,判断是否有指定元素之外的元素
def has_invalid_fields(fields):
    return bool(set(fields) - set(['foo', 'bar'])
  • defaultdict()示例
import collections
def add_animal_in_family(species, animal, family):
    species[family].add(animal)
species = collections.defaultdict(set)
add_animal_in_family(species, 'cat', 'felidea')

每次试图从字典中访问一个不存在的元素,defaultdict都会使用作为参数传入的这个函数去构造一个新值而不是抛出KeyError。

性能分析

  • 使用cProfile模块
$ python -m cProfile myscript.py

可以使用-s选项按其他字段进行排序,如-s time

  • C语言分析Valgrind以及可视化工具KCacheGrind

$ python -m cProfile -o myscript.cprof myscript.py
$ pyprof2calltree -k -i myscript.cprof

- dis模块:python字节码的反编译器

#### namedtuple和slots
- python中拥有一些固定属性的简单对象会存储所有的属性在一个字典内,这个字典本身被存在`__dict__`属性中:
```python
class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
p = Point(1, 2)
p.__dict__
p.z = 42
  • 可以通过__slots__声明来减少内存开销
class Foobar(object):
    __slots__ = 'x'
    def __init__(self, x):
        self.x = x
  • 使用python包 memory_profiler检测内存使用情况
$ python -m memory_profiler object.py

缓存加速技术

  • memoization是指通过缓存函数返回结果来加速函数调用的一种技术。仅当函数是纯函数时结果才可以被缓存,也就是说函数不能有任何副作用或输出,也不能依赖任何全局状态。
  • 基本的memoization技术
import math
_SIN_MEMOIZED_VALUES = {}
def memoized_sin(x):
    if x not in _SIN_MEMOIZED_VALUES:
        _SIN_MEMOIZED_VALUES[x] = math.sin(x)
    return _SIN_MEMOIZED_VALUES[x]
  • 使用functools.lru_cache实现
import functools
import math
@functools.lru_cache(maxsize = 2)
def memoized_sin(x):
    return math.sin(x)

使用后可以使用memoized_sin.cache_info()查看缓存情况

GIL(Global Interpreter Lock, 全局解释器锁)

利用memoryview实现浅拷贝(引用)

  • 使用memoryview可以在内存区域的任何点放入数据
ba = bytearray(8)
ba_at_4 = memoryview(ba)[4:]
#引用bytearray,从其偏移索引4到其结尾
with open("/dev/urandom", "rb") as source:
    source.readinto(ba_at_4)
    #将/dev/urandom的内容写入bytearray中从偏移索引4到结尾的位置,精确且高效地只读写了4字节

多进程与多线程

  • multiprocessing模块不仅可以有效地将负载分散到多个本地处理器上,而且可以通过它的multiprocessing.managers对象在网络中分散负载。它还提供了双向传输,以使进程间可以彼此交换信息。
  • 每次考虑在一定时间内并行处理一些工作时,最好依靠多进程创建(fork)多个作业,以便能够在多个CPU核之间分散负载。

异步和事件驱动架构

  • 事件驱动架构背后的技术是事件循环的建立。程序调用一个函数,它会一直阻塞直到收到事件。其核心思想是令程序在等待输入输出完成前保持忙碌状态,最基本的事件通常类似于“我有数据就绪可被读取”或者“我可以无阻塞地写入数据”
  • 在Unix中,用于构建这种事件循环的标准函数是系统调用select(2)或者poll(2)。它们会对几个文件描述符进行监听,并在其中之一准备好读或写时做出响应。
  • python中有select,asyncio,pyev

面向服务架构

使用ZeroMQ

RDBMS和ORM

SQLAlchemy PostgreSQL

上下文管理器

  • 上下文管理协议:

    1. 调用方法A
    2. 执行一段代码
    3. 调用方法B
  • with (open) as :执行一段代码

  • 这里希望调用方法B必须总是在调用方法A之后。open函数很好地阐明了这一模式,打开文件并在内部分配一个文件描述符的构造函数便是方法A。释放对应文件描述符的close方法就是方法B。显然,close方法总是应该在实例化文件对象之后调用。

  • contextlib标准库提供了contextmanager,通过生成器构造__enter____exit__方法,从而简化了这一机制的实现。

  • 在流水线对象上使用上下文管理器

import contextlib
class Pipeline(object):
    def _publish(self, objects):
        #image publication code here
        pass
    def _flush(self):
        #image flushing code here
        pass
    @contextlib.contextmanager
    def publisher(self):
        try:
            yield self._publish
        finally:
            self._flush()

现在,当用户在使用流水线发布某些数据时,他们无需使用_publish或者_flush。用户只需请求一个使用了名组(eponym)函数的publisher并使用它。

pipeline = Pipeline()
with pipeline.publisher() as publisher:
    publisher([1, 2, 3, 4])
    #当提供这样一个API时,就不会遇到用户错误
  • 通过一条with语句同时打开两个文件
with open("file1", "r") as source, open("file2", "w") as destination:
    destination.write(source.read())
    原文作者:treelake
    原文地址: https://www.jianshu.com/p/93414c3f8c95
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞