Python语言基础
语言特性
- 动态强类型语言(不少人误以为是弱类型)
- 动态还是静态指的是编译期还是运行期确定类型
- 强类型指的是不会发生隐式型转换
优缺点
- 胶水语言,轮子多,应用广泛
- 语言灵活,生产力高
- 性能问题,代码维护问题,Python2/3兼容问题
鸭子类型
- 关注点在对象的行为,而不是类型(duck typing)
- 比如 file,StringIO,socket 对象都支持 read/write 方法(file like object)
- 再比如定义了 __iter__ 魔术方法的对象可以用 for 迭代
monkey patch
- 所谓的 monkey patch 就是运行时替换
- 比如 gevent 库需要修改内置的 socket
- from gevent import monkey; monkey.patch_socket()
自省(Introspection)
- 运行时判断一个对象的类型的能力
- Python 一切皆对象,用 type,id,isinstance 获取对象类型信息
- Inspect 模块提供了更多获取对象信息的函数
列表或字典推导式
- 比如 [i for i in range(10) if i % 2 == 0]
- 一种快速生成 list/dict/set 的方式。用来替代 map/filter 等
- (i for i in range(10) if i % 2 == 0) 返回生成器
Python之禅
- Tim Peters 编写的关于 Python 编程的准则
- import this
- 编程拿不准的时候可以参考
Python3改进
- print 成为函数
- 编码问题。Python3 不再有 Unicode 对象,默认 str 就是 unicode
- 除法变化。Python3 除号返回浮点数
- 类型注解(type hint)。帮助 IDE 实现类型检查
- 优化 super() 方便直接调用父类函数
- 高级解包操作。a, b, *rest = range(10)
- Keyword only arguments。限定关键字参数
- Chained exceptions。Python3 重新抛出异常不会丢失栈信息
- 一切返回迭代器 range, zip, map, dict.values, etc.are, alliterators
- 生成的 pyc 文件统一放到 __pycache__
- 一些内置库的修改。urllib, selector 等
- 性能优化等
Python3新增
- yield from 链接字生成器
- asyncio 内置库,async/await 原生协程支持异步编程
- 新的内置库 enum, mock, asyncio, ipaddress, concurrent.futures 等
2/3的兼容工具
- six 模块
- 2to3 等工具转换代码
- __future__
传递参数(一个容易混淆的问题)
- 传递值还是引用呢?都不是。唯一支持的参数传递是共享传参
- Call by Object(Call by Object Reference or Call by Sharing)
- Call by sharing(共享传参)。函数形参获得实参中各个引用的副本
- 默认参数只计算一次
可变/不可变的对象
- 哪些是可变对象?哪些不可变?
- 不可变对象 bool/int/float/tuple/str/frozenset
- 可变对象 list/set/dict
- 可变对象每次赋值的时候拷贝自身的引用,而不可变对象则是创建一个新的对象
*args, **kwargs含义是什么
- 用来处理可变参数
- *args 被打包成 tuple
- **kwargs 被打包成 dict
使用异常的常见场景
- 网络请求(超时、连接错误等)
- 资源访问(权限问题、资源不存在)
- 代码逻辑(越界访问、KeyError等)
Cpython GIL
- Cpython 解释器的内存管理并不是线程安全的
- 保护多线程情况下对 Python 对象的访问
- Cpython 使用简单的锁机制避免多个线程同时执行字节码
GIL的影响
- 同一个时间只能有一个线程执行字节码
- CPU 密集程序难以利用多核优势
- IO 期间会释放 GIL,对 IO 密集程序影响不大
规避GIL影响
- CPU 密集可以使用多进程 + 进程池
- IO 密集使用多线程/协程
- cython 扩展
剖析程序性能
- 二八定律,大部分时间耗时在少量代码上
- 内置的 profile/cprofile 等工具
- 使用 pyflame(uber开源) 的火焰图工具
服务端性能优化(Web应用一般语言不会成为瓶颈)
- 数据结构与算法优化
- 数据库层:索引优化,慢查询消除,批量操作减少 IO、NoSQL
- 网络IO:批量操作,pipeline 操作减少 IO
- 缓存:使用内存数据库 redis/memcached
- 异步:asyncio,celery
- 并发:gevent/多线程
生成器
- 生成器就是可以生成值的函数
- 当一个函数里有了 yield 关键字就成了生成器
- 生成器可以挂起执行并且保持当前执行的状态
基于生成器的协程
- pep 342(Coroutines via Enhanced Generators)增强生成器功能
- 生成器可以通过 yield 暂停执行和产出数据
- 同时支持 send() 向生成器发送数据和 throw() 向生成器抛异常
协程注意点
- 协程需要使用 send(None) 或者 next(coroutine)来预激(prime)才能启动
- 在 yield 处协程会暂停执行
- 单独的 yield value 会产出值给调用方
- 可以通过 coroutine.send(value) 来给协程发送值,发送的值会赋值给 yield 表达式左边的变量 value = yield
- 协程执行完成后(没有遇到下一个 yield 语句)会抛出 StopIteration 异常
单元测试
- 针对程序模块进行正确性检验
- 一个函数,一个类进行验证
- 自底向上保证程序正确性
单元测试的重要性
- 保证代码逻辑的正确性(甚至有些采用测试驱动开发(TDD))
- 单测影响设计,易测的代码往往是高内聚低耦合的
- 回归测试,防止改一处整个服务不可用
单元测试库
- nose/pytest 较为常用
- mock 模块用来模拟替换网络请求等
- coverage 统计测试覆盖率