Python基础 知识点总结
Python是静态还是动态类型?是强类型还是弱类型?
- Python是动态强类型语言
- 动态还是静态类型是看运行时还是编译期确定类型
- 强类型指的是不会发生隐式类型转换;弱类型比如javascript,1 + ‘1’ = ‘11’,整数1会自动转换为字符1
Python作为后端语言的优缺点
- 胶水语言,轮子多,应用广泛
- 语言灵活,生产力高
- 性能问题,代码维护问题,Python2/3的兼容问题
什么是鸭子类型?
- “当看到一只鸟走起来像鸭子,游泳起来像鸭子,叫起来也像鸭子时,那么这只鸟就可以称做鸭子”
- 也就是说关注点在对象的行为,而不是类型
- 比如file,StringIO,socket,对象都支持read/write方法(file like object)
- 再比如定义了__iter__ 魔法方法的对象都可以用for迭代
什么是monkey patch?
- monkey patch其实就是运行时替换
- 比如gevent需要修改内置的socket;time.time() 替换成自己自定义的函数;
View Codefrom gevent import monkey monkey.patch_socket() import time print(time.time()) def _time(): return '321' time.time = _time print(time.time())
什么是自省?
运行时判断一个对象的类型的能力
- Python一切皆对象,可以用type,id,isinstance来获取对象类型信息
-
View Codell = [1, 2, 3, 4] d = dict(a=1) print(type(ll)) print(isinstance(d, dict)) def add(a, b): if isinstance(a, int): return a + b elif isinstance(a, str): return a.upper() + b print(add(1, 2)) print(add('head', 'tail')) print(id(ll) is id(d))
什么是列表和字典推导式?
- 比如[for i in range(10) if i % 2 ==0]
- 一种快速生成list/dict/set的方式,用来替代map/filter等
-
View Codea = ['a', 'b', 'c'] b = [1, 2, 3] print({k:v for k, v in zip(a, b)})
Python中的深拷贝与浅拷贝
-
什么是赋值操作?
- 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址 。
- 修改不可变对象(
str、tuple)需要开辟新的空间,修改可变对象(list等)不需要开辟新的空间 - 其实就是对象的引用(别名)
-
什么是浅拷贝?什么是深拷贝?
- 浅拷贝就是创建一个新的对象,其内容是原对象中元素的引用
- 深拷贝是指创建一个新的对象,然后递归的拷贝原对象所包含的子对象。深拷贝出来的对象与原对象没有任何关联。
-
Python中如何实现浅拷贝和深拷贝?
- 浅拷贝
- 使用切片[:]操作
- 使用工厂函数(如list/dir/set)
- 使用对象的copy(),copy模块中的copy()函数
-
View Codea = [1, 2, 3] b = list(a) print(id(a), id(b)) # a和b身份不同 # 4534852616 4399036872 for x, y in zip(a, b): # 但它们包含的子对象身份相同 print(id(x), id(y)) # 4395255744 4395255744 # 4395255776 4395255776 # 4395255808 4395255808 # 从上面可以明显的看出来,a 浅拷贝得到 b,a 和 b 指向内存中不同的 list 对象, # 但它们的元素却指向相同的 int 对象。这就是浅拷贝!
- 深拷贝
- 使用copy模块中的deepcopy()函数
- 浅拷贝
Python函数相关
-
参数是如何传递的?可变对象和不可变对象
- 既不是传递引用也不是值,唯一支持的参数传递是共享传参(call by object, call by sharing);
- 共享传参是指函数的各个形式参数获得实参中各个引用的副本;也就是说,函数内部的形参是实参的别名
- 不可变对象:bool/str/int/float/tuple/frozenset; 可变对象:list/set/dict
- 传递参数其实是通过对象引用的方式,实参和形参都指向同一个对象,但是Python中有可变对象和不可变对象;
对于不可变对象,每次赋值都会创建新的对象,并且原来的形参指向新的对象。
View Code
-
Python中*args, **kwarg含义是什么?
- 主要用来处理可变参数
- *args被打包成tuple
- *kwargs被打包成dict
-
View Codedef print_all(a, *args, **kwargs): print(a) if args: print(args) if kwargs: print(kwargs) print_all('hello', 'world', name='Sky')
Python的异常机制
-
什么是异常?
异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。一般情况下,在Python无法正常处理程序时就会发生一个异常。
异常是Python对象,表示一个错误。当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。
-
使用异常的常见场景
- 网络请求(超时,连接错误等)
- 资源访问(权限问题,资源不存在)
- 代码逻辑(越界访问,KeyError等)
View Codetry: f = open("testfile", "w") #可能出现异常的代码 f.write("这是一个测试文件,用于测试异常!!") except IOError: #可以捕获多个代码并处理 print("Error: 没有找到或创建文件失败或读取文件失败") # 处理异常的代码 else: print("内容写入文件成功") #异常没有发生执行的代码 finally: # 不管异常有没有发生都会执行的代码,一般用于关闭资源和释放 f.close()
-
如何自定义自己的异常?为什么需要自定义自己的异常?
- 继承Exception类实现自定义异常
- 给异常加一些附加信息
- 处理一些业务相关的特定异常(raise MyException)
-
View Codeclass MyException(Exception): pass try: raise MyException('my exception') except MyException as e: print(e)
Python性能分析及优化;GIL(全局解释器锁)
-
什么是Cpython GIL
- Cpython解释器的内存管理不是线程安全的
- 保护多线程情况下对Python对象的访问
- Cpython使用简单的锁机制避免多个线程同时执行字节码
-
GIL的影响
- 限制了程序的多核执行
- 同一个时间只有一个线程执行字节码
- CPU密集型程序难以利用多核优势
- IO期间会释放GIL,对IO密集程序影响不大(请求网页、读写文件等)
-
如何规避GIL影响
-
cpu密集可以使用多进程+进程池
-
IO密集可以使用多线程/协程
-
-
为什么有了GIL还要关注线程安全
-
什么操作是原子的?一步到位执行完?
-
一个操作如果是一个字节码指令可以完成的就是原子的(计数不是原子的, list, set, dict 在python里是原子操作。)
-
原子的可以保证线程是安全的
-
用dis操作来分析字节码
- 对非原子操作(计数等),进行加锁操作, 保证线程是安全的。
-
-
-
如何剖析程序性能
-
二八定律,大部分时间耗时在少量代码上
-
内置的profile/cprofile等工具
- 使用pyflame(uber开源)的火焰图工具 (web应用)
-
-
服务端性能优化
-
数据结构和算法优化
-
数据库层:索引优化,慢查询消除,批量操作减少IO,NoSQL
-
网络IO:批量操作,pipeline操作减少IO
-
缓存:使用缓存数据库redis/memached
-
异步:ayncio, celery
-
并发:gevnet/多线程
-
生成器与协程
-
什么是生成器?
- 生成器就是可以生成值的函数
- 当一个函数有了yield 关键字就成了生成器
- 生成器可以挂起执行并且保持当前执行的状态
-
基于生成器的协程
-
生成器可以通过yield暂停执行和产出数据
-
同时支持send()向生成器发送数据和throw()向生成器抛异常
View Codedef coro(): hello = yield 'hello' # yield关键字在=右边作为表达式,可以被send值 yield hello c = coro() # 输出hello,这里调用next产出一个值'hello',之后函数暂停 print(next(c)) # 再次调用send发送值,此时hello变量赋值为'world',然后yield产出hello变量的值'world' print(c.send('world')) #之后协程结束,后续再next(c)会抛异常StopIteartion next(c)
-
-
原生协程
-
View Codeimport asyncio async def compute(x, y): print("Compute %s + %s ..." % (x, y)) await asyncio.sleep(1.0) return x + y async def print_sum(x, y): result = await compute(x, y) print("%s + %s = %s" % (x, y, result)) loop = asyncio.get_event_loop() loop.run_until_complete(print_sum(1, 2)) loop.close()
-
Python单元测试
-
什么是单元测试
- 针对程序模块进行正确性校验
- 一个函数,一个类进行验证
- 自底向上保证程序正确性
-
为什么要写单元测试
-
保证代码逻辑的正确性
-
单测影响设计,易测的代码往往是高内聚低耦合的
-
回归测试,防止改一处整个服务不可用
-
-
单元测试相关的库
-
pytest/nose 较为常用
- mock模块用来模拟替换网络请求等
-

浙公网安备 33010602011771号