标签:分析 none 线程安全 开源 unit test test 多进程 web应用 prim
Python是静态还是动态类型?是强类型还是弱类型?
◆动态强类型语言(不少人误以为是弱类型)
◆动态还是静态指的是编译期还是运行期确定类型
◆强类型指的是不会发生隐式类型转换
为什么要用 Python?
◆胶水语言,轮子多,应用广泛
◆语言灵活,生产力高
◆性能问题、代码维护问题、 python2/3兼容问题
动态语言一时爽,代码重构火葬场。
当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
◆关注点在对象的行为,而不是类型(duck typing)
◆比如file, StringIO, socket 对象都支持read/write方法(file like object)
◆再比如定义了__iter__魔术方法的对象可以用for迭代
鸭子类型更关注接口而非类型
class Duck:
def quack(self):
print("gua gua")
class Person:
def quack(self):
print("我是人类")
def in_the_forest(duck):
duck.quack()
def game():
donald = Duck()
john = Person()
in_the_forest(donald)
in_the_forest(john)
print(type(donald))
print(type(john))
if __name__ == ‘__main__‘:
game()
什么是 monkey patch?哪些地方用到了?自己如何实现?
◆所谓的 monkey patch就是运行时替换
◆比如 gevent库需要修改内置的 socket
from gevent import monkey;monkey.patch_socket()
import socket
print(socket.socket)
print("After monkey patch")
from gevent import monkey
monkey.patch_socket()
print(socket.socket)
import select
print(select.select)
monkey.patch_select()
print("after monkey patch")
print(select.select)
# <class ‘socket.socket‘>
# After monkey patch
# <class ‘gevent._socket3.socket‘>
# <built-in function select>
# after monkey patch
# <function select at 0x03C3CE88>
import time
print(time.time()) # 1613097577.3560631
def _time():
return 1234
time.time = _time
print(time.time()) # 1234
Introspection
◆运行时判断一个对象的类型的能力
◆ Python-切皆对象,用type,id, isinstance 获取对象类型信息
◆ Inspect 模块提供了更多获取对象信息的函数
ll = [1, 2, 3]
d = dict(a=1)
print(type(ll))
print(type(d))
print(isinstance(ll, list))
print(isinstance(d, dict))
= 判断两个变量的值是不是相同
is 判断内存地址是否相同
is None
None 本身是单例
List Comprehension
◆ 比如[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) 返回生成器
The Zen of Python
◆ Tim Peters 编写的关于 Python编程的准则
◆ import this
◆编程拿不准的时候可以参考
使用 pyenv安装
多版本 Python
◆ print成为函数
◆ 编码问题。 Python3不再有 Unicode对象,默认str就是 unicode
◆ 除法变化。 Python3除号返回浮点数
◆类型注解(type hint)。帮助IDE实现类型检查
◆优化的 super0方便直接调用父类函数。
◆高级解包操作。a,b,*rest= range(10)
◆类型注解(type hint)。帮助IDE实现类型检查
◆优化的 super() 方便直接调用父类函数。
◆高级解包操作。a,b,*rest= range(10)
◆ Keyword only arguments。限定关键字参数
◆ Chained exceptions. Python3 重新抛出异常不会丟失栈信息
◆ 一切返回迭代器 range,zip,map, dict values, etc. are all iterators
◆ yield from 链接子生成器
◆ asyncio 内置库, async/await 原生协程支持异步编程
◆ 新的内置库enum, mock, asyncio, ipaddress, concurrent.futures 等
◆生成的pyc文件统一放到__pycache__
◆一些内置库的修改。 urlib,selector 等
◆性能优化等。。。
熟悉一些兼容2/3的工具
◆ six 模块
◆ 2to3等工具转换代码
◆ __future__
Python函数常考题
常考点:
一个容易混淆的问题
传递值还是引用呢?都不是。唯一支持的参数传递是共享传参
Call by Object( Call by Object Reference or Call by Sharing
Call by sharing(共享传参)。函数形参获得实参中各个引用的副本
s 创建一个新的对象 hehe
指向的同一个对象
一切是对象
对象引用的方式
实参和形参指向同一个对象
不可变对象看起来 像是拷贝了一份 实则不是 是创建了一份
创建一个新的对象,让形参指向新的
什么是 Python的异常?
Python 使用异常处理错误(有些语言使用错误码)
◆ BaseException
◆ SystemExit/KeyboardInterrupt/GeneratorExit
◆ Exception
什么时候需要捕获处理异常呢?看 Python 内置异常的类型
◆ 网络请求 (超时、连接错误等)
◆ 资源访问 (权限问题、资源不存在)
◆ 代码逻辑 (越界访问、 KeyError等)
try:
# func # 可能会抛出异常的代码
except (Exception1, Exception2) as e: # 可以捕获多个异常并处理
# 异常处理的代码
else:
# pass # 异常没有发生的时候代码逻辑
finally:
pass # 无论异常有没有发生都会执行的代码,一般处理资源的关闭和释放
如何自定义自己的异常?为什么需要定义自己的异常?
◆ 继承 Exception 实现自定义异常 (想想为什么不是 BaseException)
◆ 给异常加上一些附加信息
◆ 处理一些业务相关的特定异常(raise MyException)
class MyException(Exception):
pass
try:
raise MyException(‘my exception‘)
except MyException as e:
print(e)
CtrlC 也是异常
捕捉不到
GIL, Global Interpreter Lock
◆ Cpython解释器的内存管理并不是线程安全的
◆ 保护多线程情况下对 Python 对象的访问
◆ Cpython 使用简单的锁机制避免多个线程同时执行字节码
限制了程序的多核执行
◆ 同一个时间只能有一个线程执行字节码
◆ CPU 密集程序难以利用多核优势
◆ IO 期间会释放GIL,对IO密集程序影响不大
区分CPU和IO密集程序
◆ CPU密集可以使用多进程+进程池
◆ IO密集使用多线程/协程
◆ cython扩展
问题:请问这段代码输出?
import threading
n = [0]
def foo():
n[0] = n[0] + 1
n[0] = n[0] + 1
threads = []
for i in range(5000):
t = threading.Thread(target=foo)
threads.append(t)
for t in threads:
t.start()
print(n)
Python中什么操作才是原子的?一步到位执行完
◆ 一个操作如果是一个字节码指令可以完成就是原子的
◆ 原子的是可以保证线程安全的
◆ 使用dis操作来分析字节码
原子操作
import dis
def update_list(l):
l[0] = 1 # 原子操作,不用担心线程安全问题
dis.dis(update_list)
"""
5 0 LOAD_CONST 1 (1)
2 LOAD_FAST 0 (l)
4 LOAD_CONST 2 (0)
6 STORE_SUBSCR #单字节码操作,线程安全
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
"""
非原子操作不是线程安全的
import dis
def update_list(l):
l[0] += 1
dis.dis(update_list)
"""
5 0 LOAD_FAST 0 (l)
2 LOAD_CONST 1 (0)
4 DUP_TOP_TWO
6 BINARY_SUBSCR
8 LOAD_CONST 2 (1)
10 INPLACE_ADD #需要多个字节码操作,有可能在线程执行过程中切到其他线程
12 ROT_THREE
14 STORE_SUBSCR
16 LOAD_CONST 0 (None)
18 RETURN_VALUE
"""
使用各种 profile 工具(内置或第三方)
◆二八定律,大部分时间耗时在少量代码上
◆内置的 profile/cprofile 等工具
◆使用 pyflame(uber开源)的火焰图工具
Web应用一般语言不会成为瓶颈
◆数据结构与算法优化
◆数据库层:索引优化,慢查询消除,批量操作减少IO,NoSQL
◆网络IO:批量操作, pipeline操作减少IO
◆缓存:使用内存数据库 redis/memcached
◆异步:asyncIO, celery
◆并发:gevent/多线程
◆生成器就是可以生成值的函数
◆当一个函数里有了 yield 关键字就成了生成器
◆生成器可以挂起执行并且保持当前执行的状态
def simple_gen():
yield ‘hello‘
yield ‘world‘
gen = simple_gen()
print(type(gen)) # <class ‘generator‘>
print(next(gen)) # hello
print(next(gen)) # world
Python3 之前没有原生协程,只有基于生成器的协程
◆pep 342( Coroutines via Enhanced generators)增强生成器功能
◆生成器可以通过 yield暂停执行和产出数据
◆同时支持send() 向生成器发送数据和 throw()向生成器抛异常
Generator Based Coroutine示例
def coro():
hello = yield ‘hello‘ # yield 关键宇在右边作为表达式,可以被send值
yield hello
c = coro()
print(next(c)) # 输出 ‘hello‘,这里调用next 产出第一个值‘hello‘,之后函数暂停
print(c.send(‘world‘)) # 再次调用send发送值,此时he1lo变量值为‘ world‘,然后yie1d产出hell0变量的值‘world‘
# 之后协程结束,后续再send值会抛异常 StopIteration
#hello
#world
避免每次都要用send预激它
python2 yield 的协程
流畅的python
from functools import wraps
def coroutine(func): # 这样就不用每次都用 send(None)启动了
""" 装饰器:向前执行到第一个`yield` 表达式,预激 `func` """
@wraps(func)
def primer(*args, **kwargs):
gen = func(*args, **kwargs)
next(gen)
return gen
return primer
Python3.5 引入 async/await 支持原生协程 (native coroutine)
import asyncio
import datetime
import random
async def display_date(num, loop):
end_time = loop.time() + 50.0
while True:
print(‘Loop: {} Time: {}‘.format(num, datetime.datetime.now()))
if (loop.time() + 1.0) >= end_time:
break
await asyncio.sleep(random.randint(0, 5))
loop = asyncio.get_event_loop()
asyncio.ensure_future(display_date(1, loop))
asyncio.ensure_future(display_date(2, loop))
loop.run_forever()
Unit Testing
◆针对程序模块进行正确性检验
◆一个函数,一个类进行验证
◆自底向上保证程序正确性
三无代码不可取(无文档、无注释、无单测)
◆保证代码逻辑的正确性(甚至有些采用测试驱动开发(TDD))
◆单测影响设计,易测的代码往往是高内聚低耦合的
◆回归测试,防止改一处整个服务不可用
单元测试相关库
◆ nose/pytest 较为常用
◆ mock 模块用来模拟替换网络请求等
◆ coverage 统计测试覆盖率
深拷贝与浅拷贝的区别
◆ 什么是深拷贝?什么是浅拷贝?
◆ Python中如何实现深拷贝?
◆ 思考:Python中如何正确初始化一个二维数组?
标签:分析 none 线程安全 开源 unit test test 多进程 web应用 prim
原文地址:https://www.cnblogs.com/wenyule/p/14408004.html