一、Python
range 和 xrange
用法一致,range([start,] stop[, step])。range 返回一个list列表,xrange 返回一个生成器,取一个数据生成一个数据,性能更优。
Python数组和列表有什么区别
Python中的数组和列表具有相同的存储数据方式。但是,数组只能包含单个数据类 型元素,而列表可以包含任何数据类型元素。
当退出 Python 时是否释放所有内存分配
答案是否定的。那些具有对象循环引用或者全局命名空间引用的变量,在 Python 退出是往往不会被释放,另外不会释放 C 库保留的部分内容。
python 装饰器
是修改其他函数的功能的函数,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数。
Python 的传参是传值还是传址
Python对可变对象(字典或列表)传址,对不可变对象(数字、字符或元祖)传值。
tuple对象真的不可变吗
tuple里面的可变元素可以变,但其实变的不是tuple的元素,而是list的元素。tuple 一开始指向的list并没有改成别的list,所以,tuple所谓的“不变”是说,tuple的每个 元素,指向永远不变。即指向’a’,就不能改成指向’b’,指向一个list,就不能改成指 向其他对象,但指向的这个list本身是可变的。
那么tuple的意义何在?
元组可以在映射中当做键使用,而列表不行;元组作为很多内建函数和方法的返回值存在。
迭代器和生成器
在 Python 中,使用了 yield 的函数被称为生成器(generator)。
Python中的self
self是类的实例或对象。在Python中,self包含在第一个参数中。它有助于区分具有局部变量的类的方法和属性。init方法中的self变量引用新创建的对象,而在其他方法中,它引用其方法被调用的对象。
python的闭包函数是什么,底层怎么实现
def print_msg():
# print_msg 是外围函数
msg = "zen of python"
def printer():
# printer 是嵌套函数
print(msg)
return printer
another = print_msg()
# 输出 zen of python
another()
闭包 使得局部变量在函数外被访问成为可能。这里的 another 就是一个闭包,闭包本质上是一个函数,它有两部分组成,printer 函数和变量 msg。闭包使得这些变量的值始终保存在内存中。
闭包,顾名思义,就是一个封闭的包裹,里面包裹着自由变量,就像在类里面定义 的属性值一样,自由变量的可⻅范围随同包裹,哪里可以访问到这个包裹,哪里就 可以访问到这个自由变量。
Python中list的实现
Python中list是用下边的C语言的结构来表示的。ob_item是用来保存元素的指针数组,allocated是ob_item预先分配的内存总容量.
通常你会看到allocated的值要比ob_size的值要大。
如果这时ob_size(译者注:弹出元素后)小于allocated(译者注:已经申请的内存空间)的一半。这时申请的内存空间将会缩小。
super() 函数
通常情况下,我们在子类中定义了和父类同名的方法,那么子类的方法就会覆盖父类的方法。而super关键字实现了对父类方法的改写(增加了功能,增加的功能写在子类中,父类方法中原来的功能得以保留)。也可以说,super关键字帮助我们实现了在子类中调用父类的方法。
python 内存管理、垃圾回收机制
垃圾回收
python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略,
- 1、通过 “标记-清除” 解决容器对象可能产生的循环引用问题:第一阶段是标记阶段,GC会把所有的『活动对象』打上标记,第二阶段是把那些没有标记的对象『非活动对象』进行回收。
- 2、通过 “分代回收” 以空间换时间的方法提高垃圾回收效率:新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。
它的缺点是需要额外的空间维护引用计数,这个问题是其次的,不过最主要的问题是它不能解决对象的“循环引用”,因此,也有很多语言比如Java并没有采用该算法做来垃圾的收集机制。
Python里面如何拷⻉一个对象? (赋值,浅拷⻉,深拷⻉的区别)
-
赋值(=),就是创建了对象的一个新的引用,修改其中任意一个变量都会影响 到另一个。
-
浅拷⻉: 创建一个新的对象,但它包含的是对原始对象中包含项的引用(如果
用引用的方式修改其中一个对象,另外一个也会修改改变)- 完全切片方法(切片相当于浅拷⻉);
- 工厂函数,如list();
- copy模块的copy()函数。
-
深拷⻉:创建一个新的对象,并且递归的复制它所包含的对象(修改其中一 个,另外一个不会改变)
- copy模块的copy.deepcopy()函数
在创建新实例类型时使用浅拷⻉,并保留在新实例中复制的值。 浅拷贝用于复制引用指针,就像复制值一样。这些引用指向原始对象,并在类的任何成员中所做的更改也将影响他的原始副本。浅拷⻉允许更快地执行程序,它取决于所使用的数据的大小。
深拷⻉用于存储已复制的值。深拷⻉不会将引用指针复制到对象。它引用一个对象,并存储一些其他对象指向的新对象。原始副本中所做的更改不会影响使用该对象的任何其他副本。由于为每个被调用的对象创建了某些副本,因此深拷⻉会使程序的执行速度变慢。
copy和deepcopy区别
-
复制不可变数据类型,不管copy还是deepcopy,都是同一个地址, 当浅复制的值是不可变对象(数值,字符串,元组)时和=“赋值”的情况一样,对象的id值与浅复制原来的值相同。
-
复制的值是可变对象(列表和字典)
浅拷⻉copy有两种情况:
- 第一种情况:复制的对象中无复杂子对象,原来值的改变并不会影响浅复制的值,同时浅复制的值改变也并不会影响原来的值(因为是不同的对象)。原来值的id值与浅复制原来的值不同。
- 第二种情况:复制的对象中有复杂子对象 (例如列表中的一个子元素是一个列表), 改变原来的值中的复杂子对象的值,会影响浅复制的值。
深拷⻉deepcopy:完全复制独立,包括内层列表和字典
Python 中的__new__和__init__的区别
【同】二者均是Python面向对象语言中的函数,__new__比较少用,__init__则用的比较多。
【异】
- __init__为初始化方法, __new__方法是真正的构造函数。
- __new__是实例创建之前被调用,它的任务是创建并返回该实例,是静态方法
- __init__是实例创建之后被调用的,然后设置对象属性的一些初始值。
- 总结: __new__方法在 __init__方法之前被调用,并且 __new__方法的返回值将传递给 __init__方法作为第一个参数,最后 __init__给这个实例设置一 些参数。
Python中单下划线和双下划线分别是什么
- __name __:一种约定,Python内部的名字,用来与用户自定义的名字区分开,防 止冲突
- _name一种约定,用来指定变量私有
- __name :解释器用 _classname__name 来代替这个名字用以区别和其他类相同
的命名
魔法函数 __call__怎么使用
call 允许一个类的实例像函数一样被调用
class Entity(object):
def __init__(self, size, x, y):
self.x, self.y = x, y self.size = size
def __call__(self, x, y): # 改变实例属性
self.x, self.y = x, y
if __name__ == '__main__':
# 创建实例
demo_obj = Entity(1, 2, 3)
# 实例可以像函数那样执行,并传入x y值,修改对象的x y,对于其他定义的方法需要demo_obj.XXX(4,5)这样来使用
demo_obj(4, 5)
python中 + 和 join 的区别
在用"+"连接字符串时,结果会生成新的对象
用join时结果只是将原列表中的元素拼接起来,所以join效率比较高
Python引入其他库,底层实现是怎么样的
导入模块的本质:
import 模块名 ===》 将模块中所有的数据赋值给模块名,调用时需要模块名.方法名()
from 模块名 import 方法名 ==》将该方法单独放到当前文件运行一遍,调用时只需要方法名()即可运行。
导入包的本质:导入一个包 就是执行包下的__init__.py文件
python 多进程与多线程
from multiprocessing import Process
import os
import time
def long_time_task(i):
print('子进程: {} - 任务{}'.format(os.getpid(), i))
time.sleep(2)
print("结果: {}".format(8 ** 20))
if __name__=='__main__':
print('当前母进程: {}'.format(os.getpid()))
start = time.time()
p1 = Process(target=long_time_task, args=(1,))
p2 = Process(target=long_time_task, args=(2,))
print('等待所有子进程完成。')
p1.start()
p2.start()
p1.join()
p2.join()
end = time.time()
print("总共用时{}秒".format((end - start)))
import threading
import time
def long_time_task(i):
print('当前子线程: {} - 任务{}'.format(threading.current_thread().name, i))
time.sleep(2)
print("结果: {}".format(8 ** 20))
if __name__=='__main__':
start = time.time()
print('这是主线程:{}'.format(threading.current_thread().name))
t1 = threading.Thread(target=long_time_task, args=(1,))
t2 = threading.Thread(target=long_time_task, args=(2,))
t1.start()
t2.start()
end = time.time()
print("总共用时{}秒".format((end - start)))
random
import random
print( random.random() ) # 产生 0 到 1 之间的随机浮点数
print( random.randint(1,10) ) # 产生 1 到 10 的一个整数型随机数
print( random.uniform(1.1,5.4) ) # 产生 1.1 到 5.4 之间的随机浮点数,区间可以不是整数
print( random.choice('tomorrow') ) # 从序列中随机选取一个元素
print( random.randrange(1,100,2) ) # 生成从1到100的间隔为2的随机整数
a=[1,3,5,6,7]
print( random.sample(a, 2) ) # 从序列a中无重复采样2个元素
random.shuffle(a)# 将序列a中的元素顺序打乱
print(a)
python 删除文件:
os.remove("aa.txt")
线程进程区别 Python GIL
一个进程可以包含多个线程,这些线程可以共享当前进程中的内存空间 ,这种特性就出现了线程不安全的概念,即多个线程同时使用了一个空间,导致程序逻辑错误 ,常见的方式就是使用锁或信号量等机制来限制公共资源的使用 。
对于计算密集型的任务,多进程效率会更高一下;对于IO密集型的任务(比如文件操作,网络爬虫),采用多线程编程效率更高。
Python进阶:深入GIL
Python并不支持真正意义上的多线程,Python提供了多线程包。Python中有一个叫 Global Interpreter Lock(GIL)的东⻄,它能确保你的代码中永远只有一个线程在 执行。经过GIL的处理,会增加执行的开销。这就意味着如果你先要提高代码执行效 率,使用threading不是一个明智的选择,当然如果你的代码是IO密集型,多线程可以明显提高效率,相反如果你的代码是CPU密集型的这种情况下多线程大部分是鸡肋。
协程
协程,是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。协程在子程序内部是可中断的,然后转而执行别的子程序,在适当的时候再返回来接着执行。
协程的特点在于是一个线程执行,那和多线程比,协程有何优势?
-
极高的执行效率:因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显;
-
不需要多线程的锁机制:因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
解释一下GIL
GIL叫做全局解释器锁,主要用来保证任意时刻只有一个线程在解释器中运行来解决多线程之间数据完整性和状态同步的问题。但是会导致 python多线程编程效率很低, 所以多线程编程通常用在IO密集的情况下,其他情况下就开多进程,这样有多个GIL。同时GIL并不能完全保证数据安全, 比如当GIL在某个线程连续1000字节码或者15ms的时候, 会强制暂停这个线程对解释器的使用, 这样可能破坏原子操作的连续性。
GIL保证同一时刻只有一个线程能够运行,python有时候会有多线程编程,他的原 理是:解释器的分时复用。即多个线程的代码,轮流被解释器执行,只不过切换的 很频繁很快,给人一种多线程“同时”在执行的错觉。聊的学术化一点,其实就是 “并发”。
“并发”和“并行”的概念: 普通解释:
并发:交替做不同事情的能力 并行:同时做不同事情的能力
专业术语:
并发:不同的代码块交替执行 并行:不同的代码块同时执行
GIL的优点与缺点
GIL的优点是显而易⻅的,GIL可以保证我们在多线程编程时,无需考虑多线程之间
数据完整性和状态同步的问题。
GIL缺点是:我们的多线程程序执行起来是“并发”,而不是“并行”。因此执行效率会很低,会不如单线程的执行效率。GIL的存在使得Python多线程程序的执行效率甚至比不上单线程的执行效率。很简单,由于GIL使得同一时刻只有 一个线程在运行程序,再加上切换线程和竞争GIL带来的开销,显然Python多线程的执行效率就比不上单线程的执行效率了。
如何规避GIL带来的影响
用multiprocess(多进程)替代Thread(推荐)
multiprocess库的出现很大程度上是为了弥补thread库因为GIL而低效的缺陷。它完 整的复制了一套thread所提供的接口方便迁移。唯一的不同就是它使用了多进程而 不是多线程。每个进程有自己的独立的GIL,因此也不会出现进程之间的GIL争抢。
当然multiprocess也不是万能良药。它的引入会增加程序实现时线程间数据通讯和同步的困难。就拿计数器来举例子,如果我们要多个线程累加同一个变量,对于thread来说,申明一个global变量,用thread.Lock的context包裹住三行就搞定了。而multiprocess由于进程之间无法看到对方的数据,只能通过在主线程申明一个Queue,put再get或者用share memory的方法。
python处理多进程和多线程的底层原理
进程间通信
- 管道
- 信号量
- 共享内存
- 套接字(socket)
多态重载
读写锁 互斥锁
六、 Linux 命令
- linux 查看文件内容的命令
cat、 vi、more、less
cat -n filename 现实行号
大文件:
more 一页一页显示 space向后翻页
less 和more类似,space向后翻页,b(back)向前翻页
head -n filename 显示前n行
tail -n filename 显示后n行 - linux如何查看系统状态
查看cpu top
查看内存 free
查看进程动态 ps (process status) - Shell 快捷命令
ctrl+a 移动到当前行首home 或者 shift+home
ctrl+e 移动到当前行尾end - 打印文件的行数 wc -l
- 查看进程 ps -ef |grep
- 杀进程 kill -9
- 后台运行 screen nohub
- 设置文件权限 chmod 777 文件或目录
vi 命令
- vi如何跳转到行尾 shift+4
- vi如何跳转到行首 shift+6
- vi命令如何跳转到指定行 :100
- 跳到文本的最后一行:shift+g
- 跳到文本的第一行 先按两次 g