一.Python基础
1.Python语言特性:
动态型(运行期确定类型,静态型是编译型确定类型),强类型(不发生隐式转换,弱类型,如PHP,JavaScript就会发生隐患式转换)
2.Python作为后端语言的优缺点:
优点:
胶水语言,轮子多,应用广泛;语言灵活,生产力高
缺点:
性能问题,代码维护问题,python2/3不兼容问题
3.鸭子类型:
“当一只鸟走起来像鸭子,游泳像鸭子,叫起来像鸭子,那么这只鸟就能被称为鸭子”
关注点在对象的行为,而不是类型;
如file,StringIO,socket都支持read/write方法(file类型)
定义了__iter__就可以用for迭代
4.monkey patch(猴子补丁):
就是运行时替换
4.1比如gevent需要修改内置的socket和select:
4.2自己写的猴子补丁:
5.is和==:
is是判断是否为同一个对象(id内存地址是否相同),==是判断值是否相同
6.列表,生成器,字典推导:
7.Python之禅:
Tim Peters编写的关于Python编程的规则;
import this查看,编程拿不准可以查看:
优美胜于丑陋(Python 以编写优美的代码为目标)
明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似)
简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现)
复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁)
扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套)
间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题)
可读性很重要(优美的代码是可读的)
即便假借特例的实用性之名,也不可违背这些规则(这些规则至高无上)
不要包容所有错误,除非你确定需要这样做(精准地捕获异常,不写 except:pass 风格的代码)
当存在多种可能,不要尝试去猜测
而是尽量找一种,最好是唯一一种明显的解决方案(如果不确定,就用穷举法)
虽然这并不容易,因为你不是 Python 之父(这里的 Dutch 是指 Guido )
做也许好过不做,但不假思索就动手还不如不做(动手之前要细思量)
如果你无法向人描述你的方案,那肯定不是一个好方案;反之亦然(方案测评标准)
命名空间是一种绝妙的理念,我们应当多加利用(倡导与号召)
8.Python2/3的差异:
Python3改进:
print改为函数,可以指定sep参数;
编码问题:Python3不再有Unicode对象,默认str就是Unicode
除法变化,Python3除号(/)返回浮点数,Python2向下取整,Python3向下取整为(//)
类型注解(type hint)。帮助IDE实现类型检查;
主要是提示作用,检查可以用mypy包
优化的super()函数方便直接调用父类函数;
高级解包操作:a,b,*rest=range(10)
限定关键字参数(Keyword only arguments),防止把数据搞混
Chained exceptions。Python3重新抛出异常不会丢失栈信息(有利于排错),raise from保留栈信息(raise NotImplementedError("haha") from OSError)
一切返回迭代器range,zip,map,dict.values,etc(节省内存)
生成的pyc文件统一放到__pyache__;
一些内置库的修改,如urllib,selector等;
性能优化等
Python新增:
yield from链接子生成器;
asyncio内置库,async/await原生协程支持异步编程;
新的内置库enum(枚举),mock(单测),asyncio(异步),ipaddress(处理ip地址),concurent.futures等
Python/2/3兼容工具:
six模块;2to3等工具转换代码;__future__
9. Python函数常考题:
Python如何传递参数:
传递值还是引用?都不是,唯一支持的参数传递是共享传参;
Call by Object(Call by Object Reference or Call by Sharing)
共享传参,函数形参获得实参种各个引用的副本
ll指向[1,2,3],l也指向[1,2,3],后又指向[],所以ll仍为[1,2,3],l为[]
默认参数只计算一次
Python可变,不可变对象:
不可变对象:bool/int/float.str/frozenset;
可变对象:list/set/dict
Python *args,**kwargs:
用来处理可变参数,*args会被打包成tuple,**kwargs会打包成dict
10.Python异常处理机制:
什么是Python的异常:
Python使用异常处理错误(有些语言使用错误码,如C)
BaseException(最低层异常)
SystemExit,Timeout(超时),KeyboradInterrupt(ctrl+c),GeneratorExit(生成器退出异常),继承Exception(继承BaseException)
使用异常常见场景:
网络超时(超时,资源错误);
资源问题(权限问题,资源不存在)
代码逻辑(越界访问,KeyError等)
如何处理:
try: #fun #可能抛出异常的代码块 except (Exception1,Exception2) as e: #可以捕获多个异常并处理 #异常处理的代码 else: #pass #异常没有发生的时候代码逻辑 finally: pass #无论异常有没有发生都会执行的代码,一般处理资源的关闭和释放
如何自定义异常,作用:
通过继承Exception实现定义异常(为什么不是BaseException,ctrl+c不能结束,BaseException是所有异常的基类,KeyboradInterrupt和SystemExit,GeneratorExit,Exception都继承于它,如果继承BaseException,可能捕获不到一些异常。)
给异常加一些附加信息;
处理一些业务相关的特定异常(raise MyException)
10.Python性能分析与优化,GIL常考题:
GIL(Global Interpreter Lock):全局解释器锁
Cpython解释器的内存管理并不是线程安全的;
保护多线程情况下对Python对象的访问;
Cpython使用简单的锁机制避免多个线程同时执行字节码。
GIL的影响:
限制了程序的多核执行:
同一个时间只能有一个线程执行字节码;
CPU密集程序难以利用多核优势;
IO期间会释放GIL,对IO密集程序影响不大
如何规避GIL影响:
CPU密集可以用多进程+进程池;
IO密集使用多线程/协程
cython扩展
Python中什么操作才是原子的?一步到位执行完:
一个操作如果一个字节码指令可以完成就是原子的;
原子的是可以保证线程安全的;
使用dis操作来分析字节码。
如何剖析程序性能:
使用profile工具(内置或第三方)
二八定律:大部分时间耗时在少量代码上;
内置的profile/cprofile等工具;
使用pyflame(uber开源)的火焰图工具
服务端性能优化措施:
web应用一般语言不会成为瓶颈:
数据结构与算法优化;
数据库层:索引优化,慢查询消除,批量操作减少IO,NoSql
网络IO:批量操作,pipeline操作减少IO
缓存:使用内存数据库 redis。memcached
异步:asyncio,celery
并发:gevent/多线程
11.生成器和协程:
生成器:
生成器就是可以生成值的函数;
当一个函数里有了yield关键字就成了生成器;
生成器可以挂起并保持当前执行状态
基于生成器的协程:
Python3之前没有原生的协程,只有基于生成器的协程
pep342增强生成器功能;
生成器可以通过yield暂停执行和产出数据;
同时支持end()向生成器发送数据和throw()向生成器抛异常
协程注意点:
协程使用send(None)或者next(coroutine)来预激(prime)才能启动;
在yield出协程会暂停执行;
单独的yield value会产出值给调用方;
可以通过coroutine.send(value)来给协程发送值,发送的值会赋值给yield表达式左边的变量value=yield
协程执行完成后(没有遇到下一个yield语句)会抛出StopIteration异常
避免每次都使用send预激它(from functool import wraps)@wraps装饰器向前执行一个yield表达式,预激
Python3.5引入async/await支持原生协程
12.Python单元测试:
什么是单元测试:
针对程序模块进行正确性检验;
一个函数,一个类进行验证;
自底向上保证程序正确性
为什么要写单元测试:
三无代码不可取(无文档,无注释,无单测)
保证代码逻辑的正确性(甚至有些采用测试驱动开发(TDD))
单测影响设计,易测的代码往往是高类聚低耦合的;
回归测试,防止一处修改整个服务不可用
单元测试库:
nose/pytest较为常用;
mock模块用来模拟替换网络请求等
coverage统计测试覆盖率
Python深拷贝和浅拷贝:
深拷贝,包含对象里面的自对象的拷贝,所以原始对象的改变不会造成深拷贝里任何子元素的改变。
浅拷贝相当于拷贝了对象的引用(在python中,对象赋值实际上是对象的引用。当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了这个对象的引用)
Python创建二维数组:
https://www.cnblogs.com/woshare/p/5823303.html
二.算法和数据结构:
1.常用的内置算法结构:
sorted;
dict/list/set/tuple....
collections中常用模块:
namedtuple:让tuple属性可读(赋予名字)
deque:双端队列(可以在队列前后操作,方便实现queue/statck)
Counter:实现计数的功能
OrderDict:可以记住key第一次进入的位置(有序的)
defaultdict():键不存在时不会报错(带有默认值的字典)
dict低层使用的hash表:
为了支持快速查找使用了哈希表作为低层结构;
哈希表平均查找复杂度为O(1);
CPython解释器使用二次探查解决哈希冲突问题(如何解决哈希冲突,哈希扩容)
list vs tuple:
都是线性结构,支持下标访问;
list是可变对象,tuple是保存的引用不可变(指的是没法替换掉这个对象,但是如果对像本身是一个可变对象,是可以修改这个引用指向的可变对象的);
list没法作为字典的key,tuple可以(可变对象不可hash)
LRUCache:
Least-Recently-Used替换最少使用的对象
缓存剔除策略,当缓存空间不够用的时候需要一种方式剔除key
常见的有LRU,LFU
LRU通过使用一个循环双端队列不断把最新访问的key放到表头实现
实现:
字典利用缓存,循环双端链表用来记录访问顺序:
利用Python内置的dict+collections.OrderdDict实现;
dict用来当作k/v键值对的缓存;
OrderedDict用来实现最近访问的key
实现代码:
1 from collections import OrderedDict 2 3 class LRUCathe: 4 def __init__(self,capacity=120): 5 self.od=OrderedDict() 6 self.capacity=capacity 7 8 def get(self,key): 9 if key in self.od: 10 val=self.od[key] 11 self.od.move_to_end(key) 12 return val 13 else: 14 return -1 15 # 更新k/v 16 def put(self,key,value): 17 if key in self.od: 18 del self.od[key] 19 #更新key到表头 20 self.od[key]=value 21 else: 22 self.od[key]=value 23 #判断当前容量是否满了 24 if len(self.od)>self.capacity: 25 self.od.popitem(last=False)
2.算法常考题:
排序,查找等。。。。
排序的稳定性:
相同大小的元素排序之后保持相对的位置不变,就是稳定的。稳定性对于排序一个复杂的结构,并且保持原有排序才有意义。
快排:(分治法)
选择基准分割数组为两个字数组,小于基准和大于基准的;
对两个子数组分别快排;
合并结果。
1 '''快速排序: 2 时间平均复杂度:O(nlog2n) 3 最好情况:O(nlog2n) 4 最坏:O(n^2) 5 空间复杂度:O(nlog2n) 6 排序方式:内部排序 7 稳定性:不稳定 8 ''' 9 def quicksort(arr): 10 if len(arr)<2: 11 return arr 12 13 pivot_index=0 14 pivot=arr[pivot_index] 15 less_pivot=[i for i in arr[pivot_index+1:] if i <=pivot] 16 great_pivot=[i for i in arr[pivot_index+1:] if i > pivot] 17 return quicksort(less_pivot)+[pivot]+quicksort(great_pivot) 18 print(quicksort([3,4,5,6]))