Python小知识点

Posted by xiezg247 on November 1, 2018

语言特性

  • Python是静态还是动态类型?是强类型还是弱类型?
    1. 动态强类型语言
    2. 动态还是静态指的是在编译期还是运行期确定类型
    3. 强类型指的是不会发生隐式类型转换
  • Python作为后端语言优缺点
    1. 胶水语言,轮子多,应用广泛
    2. 语言灵活,生产力高
    3. 性能问题,代码维护,python2/3版本兼容问题
  • 鸭子类型

    1. 关注点在对象的行为,而不是类型
  • monkey patch
    1. 所谓的monkey patch就是运行时替换
    2. 比如gevent库需要修改内置的socket
      1
      2
      
        from gevent import monkey
        monkey.patch_socket()
      
  • 自省(Introspection)

    1. 运行时判断一个对象的类型的能力
    2. Python一切皆对象,用type,id,isinstance获取对象类型信息
    3. Inspect模块提供更多获取对象信息的函数
  • 列表推导

    1
    
    [i for i in range(10) if i %2 == 0]
    
  • 字典推导

    1
    2
    3
    4
    
    a = ['a', 'b']
    b = [1, 2]
    d = {k:v for k,v in zip(a,b)}
    # d = {'a': 1, 'b':2}
    

Python2/3差异

Python3改进

  • 编码,Python3默认str是unicode,不再有Unicode对象
  • print成为函数
  • 除法,Python3除号返回浮点数,Python2是整数
  • 类型注解(type hint),帮助IDE实现类型检查
  • 优化的super()方便直接调用父类函数
  • 高级解包操作,a, b , *c = range(10)
  • 限定关键字参数
  • Python3重新跑出异常不会丢失栈信息
  • 一切返回迭代器(range,zip, map,etc)
  • yield from 链接子生成器
  • asyncio内置库,async/await原生协程支持异步编程
  • 新的内置库(enum,mock,asynio,concurrent.futures,etc)
  • 生成的pyc文件统一放到__pycache__
  • 一些内置库的修改(urllib,selector,etc)
  • 性能优化等

兼容Python2/3的工具

  • six模块
  • 2to3等工具转换代码
  • __future__模块

Python如何传递参数

函数传参

  • 值传递还是值引用?唯一支持的参数传递是共享传参
  • Call by Object(Call by Object Reference or Call by Sharing)
  • 共享传参,函数形参获得实参中各个引用的副本

Python可变/不可变对象

  • 不可变对象(bool/int/float/tuple/str/frozenset)
  • 可变对象(list/set/dict)

*args, **kwargs

  • 用来处理可变参数
  • *args被打包成tuple
  • **kwargs被打包成dict

Python异常处理

什么时候需要捕获处理异常

  • 网络请求(超时、连接错误等)
  • 资源访问(权限问题、资源不存在等)
  • 代码逻辑(越界访问、KeyError等)
1
2
3
4
5
6
7
8
try:
	# 可能会抛出异常的代码 
except (Exception1, Exception2) as e:
	# 异常处理代码
else:
	# 异常没有发生时的代码逻辑
finally:
	# 无论是否有异常,都会执行的代码

如何自定义异常

  • 继承Exception实现自定义异常
  • 给异常加上一些附加信息
  • 处理一些业务相关的特定异常

Python性能分析与优化

什么是Cpython GIL(Global Interpreter Lock)

  • Cpython解释器的内存管理不是线程安全的
  • 保护多线程情况下对Python对象的访问,Cpython使用粗粒度的锁机制避免多个线程同时执行字节码

GIL的影响

  • 限制了程序的多核执行
    • 同一时间只能有一个线程执行字节码
  • CPU密集程序难以利用多核优势
  • IO密集程序影响不大,在IO期间会释放GIL

如何规避GIL影响

  • CPU密集可以使用多进程+进程池
  • IO密集使用多线程/协程
  • cython扩展

为什么有了GIL还要关注线程安全

  • 一个操作如果是一个字节码指令可以完成的就是原子的
  • 原子的可以保证线程安全的
  • 使用dis操作来分析字节码

如何剖析程序性能

  • 二八定律,大部分时间耗时在少量代码上
  • 使用profile或cprofile等内置工具
  • 使用pyflame等工具

服务端性能优化措施

  • web应用,一般语言不会成为瓶颈
  • 数据结构和算法优化
  • 数据库层:索引优化,慢查询消除,批量操作减少IO,NoSQL
  • 网络IO:批量操作,pipeline操作减少IO
  • 缓存:使用内存数据库,redis或memcached
  • 异步:asyncio,celery
  • 并发:gevent,多线程
  • 并行:多进程

Python生成器与协程

什么是生成器

  • 生成器就是可以生成值的函数
  • 当一个函数里有了yield关键字就是生成器
  • 生成器可以挂起执行并且保持当前执行的状态

基于生成器的协程(Generator Based Coroutine)

  • Python3之前没有原生协程,只有基于生成器的协程
  • 生成器可以通过yield暂停执行和产出数据
  • 同时支持send()向生成器发送数据和throw()向生成器抛出异常

协程注意点

  • 协程需要使用send()或者next(coroutine)来primer(预激)才能启动
  • 在yield处协程会暂停执行
  • 单独的yield value会产出值给调用方
  • 可以通过coroutine.send(value)来给协程发送值,发送的值会赋值给yield表达式左边的变量value = yield
  • 协会执行完成后(没有下一个yield语句)会抛出StopIteration异常

协程装饰器

避免每次都要使用send来预激

1
2
3
4
5
6
7
8
9
10
from functools import wraps

def coroutine(func):
"""装饰器 向前执行到第一个yield表达式预激func"""
	@wraps(func)
	def primer(*args, **kwargs):
		gen = func(*args, **kwargs)
		next(gen)
		return gen
	return primer

Python3原生协程

  • Python3.5引入async/await支持原生协程(native coroutine)

单元测试

什么是单元测试

  • 针对程序模块进行正确性检验
  • 一个函数,一个类型进行验证
  • 自底向上保证程序正确性

为什么要写单元测试

  • 保证代码逻辑的正确性,甚至有些公司采用测试驱动开发(TDD)
  • 单元测试影响设计,易测的代码往往是高内聚低耦合的
  • 回归测试,防止改一处代码,整个服务不可用

单元测试相关库

  • nose/pytest/unittest

  • mock模块用来模拟替换网络请求等
  • coverage统计测试覆盖率

内置数据结构和算法

数据结构/算法 语言内置 内置库
线性结构 list/tuple array/collections.nametuple
链式结构   collections.deque
字典结构 dict collections.Counter/OrderedDict
集合结构 set/frozenset  
排序算法 sorted  
二分算法   bisect
堆算法   heapq
缓存算法   functools.lru_cache(python3)

Python dict底层数据结构

  • 为了支持快速查找使用了哈希表作为底层结构
  • 哈希表平均查找时间复杂度O(1)
  • CPython解释器使用二次探查解决哈希冲突问题

Python list/tuple区别

  • 都是线性结构,支持下标访问
  • list是可变对象,tuple保存的引用不可变
  • list没法作为字典的key,tuple可以(可变对象不可hash)

排序算法

排序算法 最差时间分析 平均时间复杂度 稳定度 空间复杂度
冒泡排序 O(n^2) O(n^2) 稳定 O(1)
选择排序 O(n^2) O(n^2) 不稳定 O(1)
插入排序 O(n^2) O(n^2) 稳定 O(1)
快速排序 O(n^2) O(n*log2n) 不稳定 O(log2n)~O(n)
堆排序 O(n*log2n) O(n*log2n) 不稳定 O(1)