一, 内存管理
python 采用的是基于值的内存管理方式, 如果为不同变量赋值相同值, 则在内存中只有一份该值, 多个变量指向同一块内存地址
简述 cpython 的内存管理机制
包括如下三种机制:
(1)引用计数机制: 在 Python 中, 整数和短小的字符, Python 都会缓存这些对象, 以便重复使用. 赋值语句, 只是创造了新的引用, 而不是对象本身. 长的字符串和其它对象可以有多个相同的对象, 可以使用赋值语句创建出新的对象. 每个对象都有存有指向该对象的引用总数, 即引用计数(reference count).
(2)垃圾回收机制: 引用计数也是一种垃圾收集机制, 而且也是一种最直观, 最简单的垃圾收集技术. 当 Python 的某个对象的引用计数降为 0 时, 说明没有任何引用指向该对象, 该对象就成为要被回收的垃圾了.
(3)内存池机制: Python 的内存机制呈现金字塔形状,-1,-2 层主要有操作系统进行操作; 第 0 层是 C 中的 malloc,free 等内存分配和释放函数进行操作; 第 1 层和第 2 层是内存池, 有 Python 的接口函数 PyMem_Malloc 函数实现, 当对象小于 256K 时有该层直接分配内存; 第 3 层是最上层, 也就是我们对 Python 对象的直接操作;
Python 在运行期间会大量地执行 malloc 和 free 的操作, 频繁地在用户态和核心态之间进行切换, 这将严重影响 Python 的执行效率. 为了加速 Python 的执行效率, Python 引入了一个内存池机制, 用于管理对小块内存的申请和释放. Python 内部默认的小块内存与大块内存的分界点定在 256 个字节, 当申请的内存小于 256 字节时,
PyObject_Malloc 会在内存池中申请内存; 当申请的内存大于 256 字节时, PyObject_Malloc 的行为将蜕化为 malloc 的行为.
2. 操作系统内存管理机制(垃圾回收机制)
1. 什么是分页机制
2. 什么是分段机制
3. 分页和分段的区别
4. 什么是虚拟内存
5. 什么是内存抖动(颠簸)
6.python 的垃圾回收机制原理(好好梳理下)
2. 操作系统内存管理机制(垃圾回收机制)
1. 什么是分页机制
操作系统为了高效管理内存, 减少碎片
逻辑地址和物理地址分离的内存分配管理方案
程序的逻辑地址划分为固定大小的页(Page)
物理地址划分为同样大小的帧(Frame)
通过页表来对应逻辑地址跟物理地址
2. 什么是分段机制
分段是为了满足代码的一些逻辑需求
数据共享, 数据保护, 动态链接等
通过段表实现逻辑地址和物理地址的映射关系
每个段内存是连续内存分配, 段和段之间是离散分配的
3. 分页和分段的区别
页是出于内存利用率的角度提出的离散分配机制
段是出于用户角度, 用于数据保护, 数据隔离等用途的管理机制
页的大小是固定的, 操作系统决定; 段的大小不确定, 用户程序决定
4. 什么是虚拟内存
通过把一部分暂时不用的内存信息放到硬盘上
局部性原理, 程序运行时只有部分必须信息装入内存
内存中暂时不需要的内容放到硬盘上
系统似乎提供比实际内存大的多的容量, 称之为虚拟内存
5. 什么是内存抖动(颠簸)
本质是频繁的页调度行为
频繁的页调度, 进程不断产生缺页中断
置换一个页, 又不断再次需要这个页
运行程序太多, 页面置换策略不好. 终止进程或者增加物理内存
6.python 的垃圾回收机制原理(好好梳理下)
引用计数为主(缺点: 循环引用无法解决)
引用标记清楚和分代回收解决引用计数的问题
引用计数为主 + 标记清楚 / 分代回收为辅
二, 异常相关
代码出现以下异常的原因
IndexError: 索引异常, 如列表索引取值时, 索引不存在会抛此异常
AttributeError: 属性异常, 如实例化对象获取没有的属性就会抛出此异常
AssertionError: 断言语句失败抛出的异常
NotImplementedError: 尚未实现的方法时抛出的异常
StopIteration: 迭代器没有更多值的时候.
TypeError: 传入对象与要求不符
IndentationError: 缩进错误
异常处理写法以及如何主动抛出异常(应? 场景)
- try:
- fh = open("testfile", "w")
- try:
- fh.write("这是一个测试文件, 用于测试异常!!")
- finally:
- print "关闭文件"
- fh.close()
- except IOError:
- print "Error: 没有找到文件或读取文件失败"
raise 主动抛出一个异常
- inputValue=input("please input a int data :")
- if type(inputValue)!=type(1):
- raise ValueError
- else:
- print inputValue
python 异常机制
- BaseException
- SystemExit/KeyboardInterrupt/GeneratorExit
- Exception
使用异常的常见场景
什么时候需要捕获处理异常呢? 看 Python 内置异常的类型
网络请求(超时, 连接错误等)
资源访问(权限问题, 资源不存在)
代码逻辑(越界访问, KeyError)
try: 可能会抛出异常的代码
except: 可以捕获多个异常并处理
else: 异常没有发生的时候代码逻辑
finally: 无论异常有没有发生都会执行的代码, 一般处理资源的关闭和释放
如何自定义异常
继承 Exception 实现自定义异常(不是 BaseException)
给异常加上一些附加信息
处理一些业务相关的特定异常(raise MyException)
三, copy 和 deepcopy 的区别是什么?
python 中, 变量的存储采用了引用语义的方式, 即变量存储不是值本身, 而是值的内存地址, 对于复杂的数据结构, 如列表字典等,
变量存储的是数据结构中每个值的存储地址.
使用 copy.copy(obj)对对象 obj 进行浅拷贝, 它复制了对象, 但是对象中的元素依然使用的是原始引用, 所以只要原始引用不发生改
变, 原始引用对应的数值发生变化后, 也会影响到浅拷贝后的对象.
谈谈 Python 的深浅拷?? 以及实现? 法和应? 场景.
Python 采用基于值得内存管理模式, 赋值语句的执行过程是: 首先把等号右侧标识的表达式计算出来, 然后在内存中找一个位置把值存放进去, 最后创建变量并指向这个内存地址. Python 中的变量并不直接存储值, 而是存储了值的内存地址或者引用
简单地说, 浅拷贝只拷贝一层(如果有嵌套), 深拷贝拷贝所有层.
四, GIL 的理解
首先明确 GIL 并不是 Python 的特性, 它是在实现 Python 解析器 (CPython) 时所引入的一个概念. 每次执行 python 程序, 都会开启一个进程, 在此进程不但有主线程, 还有主线程开启的其他子线程, 同时包括垃圾回收等解释器级别的线程, 这些线程在同一个进程, 共享进程中的数据. 所有的线程访问解释器代码, 拿到执行权限, 因为解释器数据共享, 所以可能出现垃圾回收在回收解释器中数据的同时另一个线程在对数据做修改, 这样就会导致数据的不可靠. 为了解决这个问题, cpython 解释器干脆给解释器加一把互斥锁, 每个线程只有拿到解释器锁才能访问解释器代码, 其他线程等待解释器锁被释放后才能访问, 从而保证了解释器级别的数据安全.
结论: 在 Cpython 解释器中, 同一个进程下开启的多线程, 同一时刻只能有一个线程执行, 无法利用多核优势.
python 性能剖析与优化, GIL
GIL 的影响
限制了程序的多核执行
同一个时间只能有一个线程执行字节码
CPU 密集程序难以利用多核优势
IO 期间会释放 GIL, 对 IO 密集程序影响不大
规避 GIL 影响
区分 CPU 和 IO 密集程序
CPU 密集可以使用多进程 + 进程池
IO 密集使用多线程 / 协程
cython 扩展
有了 GIL 还要关注线程安全
python 中什么操作才是原子的? 一步到位执行完
一个操作如果是一个字节码指令可以完成就是原子的
原子的是可以保证线程安全的
使用 dis 模块 操作来分析字节码
五, 简述迭代器和生成器以及他们之间的区别?
迭代器就是用于迭代操作的的对象, 遵从迭代协议 (内部实现了__iter__() 和__next__()方法, 可以像列表 (可迭代对象, 只有__iter__() 方法)一样迭代获取其中的值, 与列表不同的是, 构建迭代器的时候, 不像列表一样一次性把数据加到内存, 而是以一种延迟计算的方式返回元素, 即调用 next 方法时候返回此值.
生成器本质上也是一个迭代器, 自己实现了可迭代协议, 与生成器不同的是生成器的实现方式不同, 可以通过生成器表达式和生成器函数两种方式实现, 代码更简洁. 生成器和迭代器都是惰性可迭代对象, 只能遍历一次, 数据取完抛出 Stopiteration 异常
菲波那切数列
- # 计算出前 n 个 fib 数列
- def fib(n):
- a,b = 1,1
- while n>0:
- n-=1
- yield a
- a,b = b,a+b
- print([i for i in fib(10)])
- # 计算 max 之前的 fib 数列
- def fib(max):
- a,b = 1,1
- while a <= max:
- yield a
- a,b = b,a+b
- print([i for i in fib(57)])
- # 迭代器实现菲波那切数列
- class Fib:
- def __init__(self,n):
- self.a = 1
- self.b = 1
- self.n = n
- def __iter__(self):
- return self
- def __next__(self):
- if self.n> 0:
- self.n -= 1
- value = self.a
- self.a,self.b =self.b,self.a +self.b
- return value
- else:
- raise StopIteration()
- print([i for i in Fib(10)])
列表表达式与生成器表达式的区别
列表表达式生成是一个列表属于可迭代对象, 数据一次性生成, 占用内存; 生成器表达式结果为一个生成器, 具有生成器的特性数据, 延迟计算, 一次只生成一个结果, 只能遍历一遍, 取完抛异常, 节省内存.
注意: range()属于可迭代对象, 不是迭代器或生成器, 但是是属于惰性可迭代对象, 数据是延迟加载, 娶一个生成一个, 可以重复遍历获取.
什么是装饰器? 请用装饰器实现 singleton.
装饰器的本质是一个闭包函数, 实现的功能是在不修改原函数及调用方式的情况下对原函数进行功能扩展的, 是开放封闭原则的典型代表.
- def wraper(func):
- def inner(*args,**kwargs):
- print("执行函数前扩展的内容")
- ret=func(*args,**kwargs)
- print("执行函数后扩展的内容")
- return ret
- return inner
- @wraper
- def index(name,age):
- print("我叫 %s, 今年 %s 岁" %(name,age))
- index("lcg",22)
装饰器单例:
- import threading
- class Singleton(object):
- _instance_lock = threading.Lock()
- def __init__(self):
- import time
- time.sleep(3)
- @classmethod
- def instance(cls,*args,**kwagrs):
- if not hasattr(Singleton,'_instance'):
- with Singleton._instance_lock:
- if not hasattr(Singleton, '_instance'):
- Singleton._instance = Singleton(*args,**kwagrs)
- return Singleton._instance
装饰器的写法以及应? 场景.
- import time
- def timmer(func):
- def wrapper(*args,**kwargs):
- start_time=time.time()
- res=func(*args,**kwargs)
- stop_time=time.time()
- print('run time is %s' %(stop_time-start_time))
- return res
- return wrapper
- @timmer
- def foo():
- time.sleep(3)
- print('from foo')
- foo()
- def auth(driver='file'):
- def auth2(func):
- def wrapper(*args,**kwargs):
- name=input("user:")
- pwd=input("pwd:")
- if driver == 'file':
- if name == 'guang' and pwd == '123':
- print('guang successful')
- res=func(*args,**kwargs)
- return res
- elif driver == 'ldap':
- print('ldap')
- return wrapper
- return auth2
- @auth(driver='file')
- def foo(name):
- print(name,'welcome!')
- foo('guang')
无参装饰器在用户登录 认证中常见
有参装饰器在 flask 的路由系统中见到过
- @App.route('/')
- def index():
- return 'Hello World!'
- def route(self, rule, **options):
- def decorator(f):
- endpoint = options.pop("endpoint", f.__name__)
- self.add_url_rule(rule, endpoint, f, **options)
- return f
- return decorator
如何理解? 成器和迭代器?
生成器:
Python 使用生成器对延迟操作提供了支持. 所谓延迟操作, 是指在需要的时候才产生结果, 而不是立即产生结果. 这也是生成器的主要好处.
1. 语法上和函数类似: 生成器函数和常规函数几乎是一样的. 它们都是使用 def 语句进行定义, 差别在于, 生成器使用 yield 语句返回一个值, 而常规函数使用 return 语句返回一个值
2. 自动实现迭代器协议: 对于生成器, python 会自动实现迭代器协议, 以便应用到迭代背景中(for 循环, sum 函数), 由于生成器自动实现了迭代器协议, 所以, 我们可以调用它的 next 方法, 并且, 在没有值可以返回的时候, 生成器自动产生 Stoplteration 异常
3. 状态挂起: 生成器使用 yield 语句返回一个值. yield 语句挂起该生成器函数的状态, 保留足够的信息, 以便之后从它离开的地方继续执行
生成器本质上还是一个迭代器
迭代器:
顾名思义, 迭代器就是用于迭代操作 (for 循环) 的对象, 它像列表一样可以迭代获取其中的每一个元素, 任何实现了__next__方法的对象都可以成为迭代器
它与列表的区别在于, 构建迭代器的时候, 不像列表把所有元素一次性加载到内存, 而是以一种延迟计算 (lazy evaluation) 方式返回元素. 比如列表含有一千万个整数, 需要占超过 400MB 的内存, 而迭代器只需要几十个字节的空间. 因为它并没有把所有元素装载到内存中, 而是等到调用 next 方法时候才返回该元素(按需调用 call by need 方式, 本质上 for 循环就是不断地调用迭代器的 next 方法)
补充点: 迭代器协议与可迭代对象
1. 迭代器协议是指: 对象需要提供 next 方法, 它要么返回迭代中的下一项, 要么就引起一个 stoplteration 异常, 以终止迭代
2. 可迭代对象就是: 实现了迭代器协议的对象
3. 协议是一种约定, 可迭代对象实现迭代器协议, python 的内置工具 (如 for 循环, sum,min,max 函数等) 使用迭代器协议访问对象
谈谈你对闭包的理解?
- def foo():
- m=3
- n=5
- def bar():
- a=4
- return m+n+a
- return bar
- >>>bar = foo()
- >>>bar()
说明:
bar 在 foo 函数的代码块中定义. 我们称 bar 是 foo 的内部函数.
在 bar 的局部作用域中可以直接访问 foo 局部作用域中定义的 m,n 变量.
简单的说, 这种内部函数可以使用外部函数变量的行为, 就叫闭包.
闭包的意义与应用:
- # 闭包的意义: 返回的函数对象, 不仅仅是一个函数对象, 在该函数外还包裹了一层作用域, 这使得, 该函数无论在何处调用, 优先使用自己外层包裹的作用域
- # 应用领域: 延迟计算(原来我们是传参, 现在我们是包起来)
- from urllib.request import urlopen
- def index(url):
- def get():
- return urlopen(url).read()
- return get
- baidu=index('http://www.baidu.com')
- print(baidu().decode('utf-8'))
装饰器就是闭包函数的一种应用场景
六, 函数相关
lambda 表达式格式以及应? 场景?
*arg 和 **kwarg 作?
可变类型作为参数 / 不可变类型作为参数
考察点: 传参方式; 可变对象
python 如何传递参数
python 可变 / 不可变对象
python 可变参数作为默认参数 默认参数只计算一次
lambda 表达式格式以及应? 场景?
编程中提到的 lambda 表达式, 通常是在需要一个函数, 但是又不想费神去命名一个函数的场合下使用, 也就是指匿名函数, lambda 是一个表达式..
map( lambda x: x*x, [y for y in range(10)] )
因为后者多定义了一个 (污染环境的) 函数, 尤其如果这个函数只会使用一次的话. 而且第一种写法实际上更易读, 因为那个映射到列表上的函数具体是要做什么, 非常一目了然. 如果你仔细观察自己的代码, 会发现这种场景其实很常见: 你在某处就真的只需要一个能做一件事情的函数而已, 连它叫什么名字都无关紧要. Lambda 表达式就可以用来做这件事.
*arg 和 **kwarg 作?
这是一种特殊的语法, 在函数定义中使用 * args 和 kwargs 传递可变长参数. *args 用作传递非命名键值可变长参数(位置参数), 如列表元祖; kwargs 用作传递键值可变长参数, 如字典.
可变类型作为参数 / 不可变类型作为参数
考察点: 传参方式; 可变对象
python 如何传递参数
一个容易混淆的问题
传递值还是引用呢? 都不是. 唯一支持的参数传递是共享传参
call by object
call by sharing(共享传参). 函数形参获得实参中各个引用的副本
python 可变 / 不可变对象
不可变对象 bool/int/float/tuple/str/frozenset 新对象
可变对象 list/set/dict
可变对象作为函数参数传递 传递参数是通过对象形式来传递的, 实参和形参都是对象得区分对象
python 可变参数作为默认参数 默认参数只计算一次
python *args,**kwargs
用来处理可变参数
*args 被打包成 tuple
**kwargs 被打包成 dict
七, Python 中变量的作用域(变量的查找顺序)
python 中的作用域分 4 种情况:
(1)L:local, 局部作用域, 即函数中定义的变量;
(2)E:enclosing, 嵌套的父级函数的局部作用域, 即包含此函数的上级函数的局部作用域, 但不是全局的;
(3)G:globa, 全局变量, 就是模块级别定义的变量;
(4)B:built-in, 系统固定模块里面的变量, 比如 int, bytearray 等.
搜索变量的优先级顺序依次是: 局部作用域>外层作用域>当前模块中的全局 > python 内置作用域, 也就是 LEGB.
解释 python 脚本程序的 "__name__" 变量及其作用
每一个 python 程序脚本在运行的时候, 都有一个__name__属性, 如果程序是作为模块被引入的, 则其__name__属性值则自动被设置为模块名, 如果脚本程序独立运行, 则其__name__属性则自动被设置为__main__, 利用__name__属性即可控制 python 程序的运行方式.
解释 python 字符串驻留机制.
Python 支持字符串驻留机制, 即: 对于短字符串, 将其赋值给多个不同的对象时, 内存中只有一个副本, 多个对象共享该副本. 这一点不适用于长字符串, 即长字符串不遵守驻留机制, 下面的代码演示了短字符串和长字符串在这方面的区别.
- a = "123"
- b = "123"
- print(id(a) == id(b)) # True
- c = "123" * 50
- d = "123" * 50
- print(id(c) == id(d)) # False
常? 字符串格式化有哪些?
1. 占位符 %
%d 表示那个位置是整数;%f 表示浮点数;%s 表示字符串.
如何判断是函数还是? 法?
- print(isinstance(obj.func, FunctionType)) # False
- print(isinstance(obj.func, MethodType)) # True
什么是断?? 应? 场景?
python assert 断言是声明其布尔值必须为真的判定, 如果发生异常就说明表达示为假
比如我想测试 a==1. 就可以用断言. 如果我的猜想错误就会抛出异常, 可以用于测试一段表达式是否成立.
- >>> a = 1
- >>> assert a==1
- >>> assert a==2
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- AssertionError
应用场景: 一般就是用于测试, 人总会犯错. 在不确定一个表达式是否城区的是
is 和 == 的区别
is 比较的是两个实例对象是不是完全相同, 它们是不是同一个对象, 占用的内存地址是否相同. 莱布尼茨说过:"世界上没有两片完全相同的叶子", 这个 is 正是这样的比较, 比较是不是同一片叶子(即比较的 id 是否相同, 这 id 类似于人的身份证标识).
== 比较的是两个对象的内容是否相等, 即内存地址可以不一样, 内容一样就可以了. 这里比较的并非是同一片叶子, 可能叶子的种类或者脉络相同就可以了. 默认会调用对象的 __eq__()方法.
来源: http://www.bubuko.com/infodetail-3114781.html