Python语言基础
Note: This blog is created to Prepare For Intern Interviews
python语言特性
静态or动态?强类型or弱类型?
- 动态强类型语言
- 动态or静态:编译期还是运行期确定类型
- 动态:没有类型申明
- 强类型:不会发生隐式类型转换
- JS, PHP就是弱类型(eg. 数字和字符串可以相加)
Python作为后端语言优缺点
- 轮子多,胶水语言,应用广泛
- 语言灵活
- 性能问题、代码维护问题、python2/3兼容问题
鸭子类型
- 关注点在对象的行为,而不是类型(duck typing)
- file/stringio/socket对象都支持read/write方法
- 定义了__iter__魔术方法的对象可以用for迭代
- 更关注接口(方法)而不是类型
monkey patch
- 运行时的属性替换
- e.g. gavent库修改内置的socket(堵塞变成非阻塞)
自省(introspection)
- 运行时判断一个对象类型的能力
- 一切皆对象,用type,id,isinstance来获取对象类型信息
- inspect模块提供了更度获取对象信息的函数
列表&字典推导
- list comprehension
- 快速生成list/dict/set的方式。用来替代map/filter等
- ( i for i in range(10) if i % 2 == 0)返回生成器可以大大节省内存
- 可以简化操作:
a = ['a', 'b', 'c']
b = [1, 2, 3]
d = {k: v for k, v in zip(a,b)}
python之禅
- import this
- 动态语言编写大型项目的时候维护性差,重构很难
python2/3差异
python2/3差异
- pyenv支持本地装多个python版本
- python3改进
- 类型注解(type hint),帮助IDE实现类型检查。e.g.
def f(name:str) -> str:
- 优化的super()方便直接调用父类函数 e.g. python2:
super(<子类名>, self).f()
, python3:super().f()
- 高级解包操作。
a, b, *c = range(10)
–>a = 0, b = 1, c = [2,3, ..., 9]
- 编码问题。python2默认总结,python3不再有unicode对象,默认str就是unicode。做传输的时候,需要把unicode encode成字节串,当拿到字节串的时候要decode成unicode。
- 除法变化:‘/’结果是浮点数,要用‘//’ 才能做一个截断
- from future import print_function
- print成为函数:print (?)
print ('a', 'b', sep = '|'). >>> a|b
- 限定关键字参数:
def f(a, b, *, c)
(*后面的必须要传递名字) - 一切返回迭代器 range,zip,map,dict.values, etc. (e.g. range()返回可迭代对象,不会立马生成列表)
- chained exceptions:有利于排错。原来:从一个异常里面再raise一个异常,原来的异常就会丢失。
- 生成的pyc文件统一放到一个文件中
- 内置库的修改 urllib, selector等
- 性能优化
- 类型注解(type hint),帮助IDE实现类型检查。e.g.
python3新增
- yield from 链接子生成器
- asyncio内置库,async/await 原生协程支持异步编程
- 新的内置库 enum, mock, asyncio, ipaddress, concurrent.futures 等
python2/3工具
- six模块
- 2to3等工具
- __future__
python函数常考
python传递参数
- 共享传参(call by sharing)
- 形参和实参都是指向对象
- 不可变(拷贝)/可变(引用)对象
- 不可变对象:bool/int/float/tuple/str/frozenset
- 可变对象list/set/dict
【例题】输出结果是什么?
def f(l):
l = []
print ('l:', l)
print (id(l))
ll = [1,2]
f(ll)
print ('ll:',ll)
print (id(ll))
l: []
4491653000
ll: [1, 2]
4491652936
【例题】python可变参数作为默认参数,结果是什么?
def f(l=[1]):
l.append(1)
print (l)
f()
f()
[1, 1]
[1, 1, 1]
原因:默认参数只计算一次(参考文档:https://www.cnblogs.com/zwgblog/p/7194950.html)
*args, **kwargs
- 用来处理可变参数(参数个数不确定)
- *arg(可变的参数)被打包成tuple
- **kwargs(可变的关键字参数) 被打包成dict
- 传参方式
- 直接传递
- 用一个*打包,传list
- 使用两个*打包,传dict
Python异常机制
python使用异常处理错误
- BaseException(所有都继承这个异常)
- SystemExit
- KeyboardInterrupt
- GeneratorExit
- exception
- 自定义的时候都是继承这个类
异常常见的场景
- 网络请求(超时、连接错误)
- 资源访问(权限、资源不存在)
- 代码逻辑(越界访问、keyerror)
try:
# func # 可能会抛出异常的代码
except (Exception1, Exception2) as e # 可以捕获多个异常处理
# 异常处理代码
else:
# pass # 异常没有发生的时候代码逻辑
finally:
# pass # 无论异常有没有发生都会执行的代码,一般处理资源的关闭和释放
自定义异常
- 继承exception实现自定义异常(为什么不是BaseException? control-c就不能终止了)
- 给异常加上一些附加信息
- 自定义:
raise(myexception)
python性能分析与优化(GIL)
GIL(global interpreter lock)
- cpython解释器的内存管理并不是
线程安全
的 - 保护多线程情况下对python对象的访问
- cpython使用锁机制避免多线程同时执行字节码
gil影响:限制了程序的多核执行
- 同一时间只能有一个线程执行字节码
- cpu密集程序难以利用多核优势
- io期间会释放GIL, 对IO密集程序影响不大
规避gil影响:区分cpu还是io密集
- cpu密集:大量时间花在计算上,多进程+进程池
- io密集:大量时间花在传输上,使用多线程/协程
- cython扩展(python转成c++)
为什么有了gil还要关注线程安全
- 一个操作如果是
一个字节码指令
可以完成的就是原子
的 - 原子的是可以保证线程安全的
- 使用dis操作来分析字节码
- 编码:
lock = thread.lock()
和with lock
如何剖析程序性能:使用profile工具
- 二八定律,大部分时间耗时在少量代码
- 内置的profele工具
- 使用pyflame的火焰图工具
服务端性能优化措施
web应用一般语言不会成为瓶颈
- 数据结构&算法
- 数据库层:索引优化、慢查询、nosql、批量操作减少io
- 网络io:批量操作、pipeline操作、减少io
- 缓存:使用内存数据库 redis/memcached
- 异步:asyncio,celery。异步编程就是把耗时的操作放进一个单独的线程中进行处理(该线程需要将执行进度反映到界面上)。由于耗时操作是在另外一个线程中被执行的,所以它不会堵塞主线程。主线程开启这些单独的线程后,还可以继续执行其他操作(例如窗体绘制等);异步编程可以提高用户体验,避免在进行耗时操作时让用户看到程序“卡死”的现象)
- 并发:gevent/多线程
python生成器与协程
生成器 generator
- 可以生成值的函数
- 当函数里有
yield
关键字 - 可以挂起执行,并产出数据
基于生成器的协程
- pep342 增强生成器功能
- 生成器可以通过yield暂停执行和产出数据
- 同时支持send()向生成器发送数据和throw()向生成器抛出异常
协程
- 需要send或者next来预激才能启动(可以使用协程装饰器避免每次都要用send预激他)
- 在yield处协程会暂停执行
- python3.5引入async/await支持原生协程(native coroutine)
python单元测试(unit testing)
- 动态语言需要python的单元测试来保证代码质量
- 针对程序模块进行正确性
- 一个函数,一个类进行验证
- 自底而上
防止三无代码(文档、注释、单测)
- 保证代码逻辑正确性(甚至测试驱动开发tdd)
- 易测的代码往往是高内聚低耦合的
- 回归测试防止改一处导致整个服务不可用
单元测试的库
- nose/pytest
- mock模块来模拟替换网络请求等
- coverage统计测试覆盖
设计测试用例
- 正常值
- 边界值
- 异常值(非法、none、空值等)
python深拷贝&浅拷贝
- 深拷贝:a = b;两者的值不受影响
- 浅拷贝:a = b;b的值变了,a也变了
- 初始化二维数组:
- 给数组的内容赋值,而不是一个数组a = 数组b这样的赋值方法
- deep_copy