Python 知识总结与常见问题
Python 解释型语言
计算机是不能够识别高级语言的,所以当我们运行一个高级语言程序的时候,就需要一个"翻译机"来把高级语言转变成计算机能读懂的机器语言的过程。这个过程分成两类,第一种是==编译==,第二种是==解释==。
编译型语言 vs 解释型语言
- 编译型语言:编译型语言在程序执行之前,先会通过编译器对程序执行一个编译的过程,把程序转变成机器语言。运行时就不需要翻译,而直接执行就可以了。最典型的例子就是C语言。
- 解释型语言:解释型语言就没有这个编译的过程,而是在程序运行的时候,通过解释器对程序逐行作出解释,然后再运行。
Python 声明变量
python a = 1 和C++的int a = 1有啥区别?
-
python是==弱类型的,动态类型的语言==,变量无需声明可以直接赋值;而且数据类型可以随时改变;C++是强类型的,静态类型的,变量声明之前要声明类型。
-
整数、字符串等等,在C中成为变量,而在python中,他们是对象。而变量名只是对象的引用。举个例子,a=3,在内存中建立了一个值为3的整数对象,而a是指向这个对象的引用,如果某个对象没有引用指向他,就会自动回收空间。动态类型:对象的类型运行过程中确定。
Python 深拷贝浅拷贝
都是对象的拷贝,生成一个看似相同的对象。
但是==浅拷贝==是将一个对象的引用赋值给另一个对象,如果修改被赋值对象的内容,原对象也受到影响。
==深拷贝==是将原对象的值拷贝给新对象,修改新对象的内容,不影响原对象。
列表而言,调用copy方法或者直接把列表名赋值给新变量,是浅拷贝;调用deepcopy方法,或者用列表切片的方法,是深拷贝。
列表和元组的区别
列表是==可变==元素,元组是==不可变==元素。
元组元素对象的引用不可变,不能对其再次赋值,但是在其中可变元素对象的引用不被修改前提下,仍旧可以对可变元素对象修改。
相关内容参见 Python 基础 - 列表 和 Python 基础 - 元组
函数参数 args 和 *kwargs 的区别
-
有时候不知道可能会传入多少个实参;方便传入任意数量的实参。==args==是元组参数,将非键值对的多个位置参数*打包成元组给函数调用;
-
有时候不知道函数可能会传入什么类型的实参,==kwargs==是字典参数,将键值对的关键字参数**打包成字典给函数体调用。
-
传参的时候,args要在kwargs前面,否则会报错。
-
* 的作用是打包 / 拆包
(5条消息) python中args和kwargs的理解_千千Sama的博客-CSDN博客_python中的*kwargs
相关内容参见 函数 - 不定长参数
字典及其底层实现
- 字典是一种可变的、无序的容器结构,元素以键值对的形式存在;特点是搜索速度快,数据量增加10000倍,搜索时间增加不到2倍。
- 底层实现是通过==哈希表==,哈希表是带索引和存储空间的表,对于可哈希的对象,通过哈希运算、取余,获得哈希索引,在该索引对应的空间进行操作。
- 解决哈希冲突的方法:线性探测法、再哈希法、拉链法等等。
相关内容参见 Python 基础 - 字典
迭代器 生成器
-
==迭代器==是一种支持next()操作的对象。它包含了一组元素,当执行next()操作时,返回其中一个元素。当所有元素都被返回后,再执行next()报异常---StopIteration。是访问集合元素的一种方式。
-
在 Python 中,使用了 yield 的函数被称为==生成器(generator)==。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作。在运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
GIL锁
GIL 全局解释器锁
- 在Cpython中,内存管理不是线程安全的,避免多个线程间同时访问同一对象,需要GIL锁。
- 每个线程在执行过程中,需要先获取GIL,保证同一时刻只有一个线程可以执行代码。
- 线程释放GIL锁:在IO操作等可能遇到引起阻塞的情况前,可以暂时释放GIL锁,但在执行完毕之后,必须重新获取GIL。
- 多线程在处理存在IO情况的时候比单线程优秀,因为遇到IO阻塞的时候会释放GIL锁。
- python实现多线程:使用
threading模块。
- 多线程的时候,保证共享数据有序的进行修改,避免造成混乱。导入线程模块,通过 threading.Lock() 创建互斥锁。
装饰器
- python装饰器的本质就是一个==函数闭包==的语法糖;在第一次调用被装饰函数的时候,调用函数闭包进行函数增强。可以让其他函数在不变动代码的前提下,增加额外功能。通过装饰器可以抽离出大量与函数功能无关的代码并重用。
- 函数闭包本质是一个函数,只不过他的参数和返回值都是函数。返回的函数是对传入的函数增强后的结果。
- 语法糖就是计算机语言中添加某种语法,不改变程序功能,但更方便程序员使用。通过
@闭包函数名,python解释器自动调用闭包函数对原函数进行增强。
相关内容参见 函数 - 闭包 和 OOP - 类装饰器
# 举个例子 对一个计数程序 增加统计时间功能
import time
def count_time_wrapper(func):
# 闭包,增加统计时间的功能
def improved_func(*args, **kwargs): # 这里的增强函数,后面调用的时候,应该把原函数的参数在这里传递给原函数执行。
start_time = time.clock()
ret = func(*args, **kwargs) # 调用有参数的原函数 获得返回值
end_time = time.clock()
print("takes {} s".format(end_time - start_time))
return ret # 增强函数的返回值就是原函数的返回值
return improved_func
@count_time_wrapper
def odds(lim):
for i in range(0,lim,2):
print(i)
if __name__ == '__main__':
# 装饰器等价于在函数调用之前执行如下语句:
# odds = count_time_wrapper(odds)
odds(100)
lambda 匿名函数
- ==lambda==可以定义一个匿名函数;def定义的函数必须有一个名字。这应该是lambda与def两者最大的区别。
- lambda函数主要用来写一些小体量的一次性函数,使代码更加简洁。比如用在map方法里面。
Python 和 C++ 继承的区别?
- 访问控制方面,python并没有C++的public protected private的定义
相关内容参见 OOP - 类的访问控制
Python 异常处理
==try..except..else..finally==语句;检测try语法块中的错误,让except捕获语法错误并处理。(没有异常发生,执行else;不论有没有都执行finally)
常见错误:KeyError IndexError TypeError SyntaxError ValueError等等
也可以用raise自己引发一个异常。
相关内容参见 Python 异常处理
Python class的理解
-
类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。
-
类由三个部分组成:类名、类的属性、类的方法
- 类的对象支持三种操作:实例化、属性引用、方法调用。
相关内容参见 Python 面向对象编程
Python 多继承
- 多继承带来路径选择问题,究竟继承哪个父类的特征
- python使用==MRO(method resolution order)==解决基类搜索顺序问题
Python 保护继承
Python map是有序的吗
Python 中的库函数和模块有哪些?请举几例。
答:time、os、copy、sys、pickle、glob、math、random等。
Python 中内存泄漏和溢出指的是什么,有什么区别?
答:(1)==内存泄漏(Memory Leak)==是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。对于python这种支持垃圾回收的语言来说,也会有内存泄露,大致有三种原因:
Python 内存泄漏的三种原因
- 所用到的用 C 语言开发的底层模块中出现了内存泄露。
- 代码中用到了全局的list、dict或其它容器,不停的往这些容器中插入对象,而忘记了在使用完之后进行删除回收。
- 代码中有"引用循环",并且被循环引用的对象定义了
__del__方法,就会发生内存泄露。
诊断内存是否泄露用到可用gc模块和objgraph模块。gc模块是Python的垃圾收集器模块,而objgraph 是一个用于诊断内存问题的工具。当在程序中选出诊断点后,插入以下语句:
import gc
import objgraph
### 强制进行垃圾回收
gc.collect()
### 打印出对象数目最多的 50 个类型信息
objgraph.show_most_common_types(limit=50)