目录
Python高级
元类
42.Python中类方法、类实例方法、静态方法有何区别?
43.遍历一个object的所有属性,并print每一个属性名?
44.写一个类,并让它尽可能多的支持操作符?
45.介绍Cython,Pypy Cpython Numba各有什么缺点
46.请描述抽象类和接口类的区别和联系
47.Python中如何动态获取和设置对象的属性?
内存管理与垃圾回收机制
48.哪些操作会导致Python内存溢出,怎么处理?
49.关于Python内存管理,下列说法错误的是 B
50.Python的内存管理机制及调优手段?
51.内存泄露是什么?如何避免?
函数
52.python常见的列表推导式?
53.简述read、readline、readlines的区别?
54.什么是Hash(散列函数)?
55.python函数重载机制?
56.写一个函数找出一个整数数组中,第二大的数
57.手写一个判断时间的装饰器
58.使用Python内置的filter()方法来过滤?
59.编写函数的4个原则
60.函数调用参数的传递方式是值传递还是引用传递?
61.如何在function里面设置一个全局变量
62.对缺省参数的理解 ?
63.Mysql怎么限制IP访问?
64.带参数的装饰器?
65.为什么函数名字可以当做参数用?
66.Python中pass语句的作用是什么?
67.有这样一段代码,print c会输出什么,为什么?
68.交换两个变量的值?
69.map函数和reduce函数?
70.回调函数,如何通信的?
71.Python主要的内置数据类型都有哪些? print dir( ‘a ’) 的输出?
72.map(lambda x:xx,[y for y in range(3)])的输出?
73.hasattr() getattr() setattr() 函数使用详解?
74.一句话解决阶乘函数?
75.什么是lambda函数? 有什么好处?
76.递归函数停止的条件?
77.下面这段代码的输出结果将是什么?请解释。
78.什么是lambda函数?它有什么好处?写一个匿名函数求两个数的和
设计模式
79.对设计模式的理解,简述你了解的设计模式?
80.请手写一个单例
81.单例模式的应用场景有那些?
82.用一行代码生成[1,4,9,16,25,36,49,64,81,100]
83.对装饰器的理解,并写出一个计时器记录方法执行性能的装饰器?
84.解释以下什么是闭包?
85.函数装饰器有什么作用?
86.生成器,迭代器的区别?
87.X是什么类型?
88.请用一行代码 实现将1-N 的整数列表以3为单位分组
89.Python中yield的用法?
面向对象
90.Python中的可变对象和不可变对象?
91.Python的魔法方法
92.面向对象中怎么实现只读属性?
93.谈谈你对面向对象的理解?
正则表达式
94.请写出一段代码用正则匹配出ip?
95.a = “abbbccc”,用正则匹配为abccc,不管有多少b,就出现一次?
96.Python字符串查找和替换?
97.用Python匹配HTML g tag的时候,<.> 和 <.*?> 有什么区别
98.正则表达式贪婪与非贪婪模式的区别?
99.写出开头匹配字母和下划线,末尾是数字的正则表达式?
100.正则表达式操作
101.请匹配出变量A 中的json字符串。
102.怎么过滤评论中的表情?
103.简述Python里面search和match的区别
104.请写出匹配ip的Python正则表达式
105.Python里match与search的区别?
系统编程
106.进程总结
107.谈谈你对多进程,多线程,以及协程的理解,项目是否用?
108.Python异常使用场景有那些?
109.多线程共同操作同一个数据互斥锁同步?
110.什么是多线程竞争?
111.请介绍一下Python的线程同步?
112.解释以下什么是锁,有哪几种锁?
113.什么是死锁?
114.多线程交互访问数据,如果访问到了就不访问了?
115.什么是线程安全,什么是互斥锁?
116.说说下面几个概念:同步,异步,阻塞,非阻塞?
117.什么是僵尸进程和孤儿进程?怎么避免僵尸进程?
118.python中进程与线程的使用场景?
119.线程是并发还是并行,进程是并发还是并行?
120.并行(parallel)和并发(concurrency)?
121.IO密集型和CPU密集型区别?
122.python asyncio的原理?
网络编程
123.怎么实现强行关闭客户端和服务器之间的连接?
124.简述TCP和UDP的区别以及优缺点?
125.简述浏览器通过WSGI请求动态资源的过程?
126.描述用浏览器访问www.baidu.com的过程
127.Post和Get请求的区别?
129.列出你知道的HTTP协议的状态码,说出表示什么意思?
130.请简单说一下三次握手和四次挥手?
131.说一下什么是tcp的2MSL?
132.为什么客户端在TIME-WAIT状态必须等待2MSL的时间?
133.说说HTTP和HTTPS区别?
134.谈一下HTTP协议以及协议头部中表示数据类型的字段?
135.HTTP请求方法都有什么?
136.使用Socket套接字需要传入哪些参数 ?
137.HTTP常见请求头?
138.七层模型?
139.url的形式?
类方法: 是类对象的方法,在定义时需要在上方使用 @classmethod 进行装饰,形参为cls,表示类对象,类对象和实例对象都可调用
类实例方法: 是类实例化对象的方法,只有实例对象可以调用,形参为self,指代对象本身;
静态方法: 是一个任意函数,在其上方使用 @staticmethod 进行装饰,可以用对象直接调用,静态方法实际上跟该类没有太大关系
class Car: def __init__(self,name,loss): # loss [价格,油耗,公里数] self.name = name self.loss = loss def getName(self): return self.name def getPrice(self): # 获取汽车价格 return self.loss[0] def getLoss(self): # 获取汽车损耗值 return self.loss[1] * self.loss[2] Bmw = Car("宝马",[60,9,500]) # 实例化一个宝马车对象 print(getattr(Bmw,"name")) # 使用getattr()传入对象名字,属性值。 print(dir(Bmw)) # 获Bmw所有的属性和方法
class Array: __list = [] def __init__(self): print "constructor" def __del__(self): print "destruct" def __str__(self): return "this self-defined array class" def __getitem__(self,key): return self.__list[key] def __len__(self): return len(self.__list) def Add(self,value): self.__list.append(value) def Remove(self,index): del self.__list[index] def DisplayItems(self): print "show all items---" for item in self.__list: print item
Cython
1.抽象类: 规定了一系列的方法,并规定了必须由继承类实现的方法。由于有抽象方法的存在,所以抽象类不能实例化。可以将抽象类理解为毛坯房,门窗,墙面的样式由你自己来定,所以抽象类与作为基类的普通类的区别在于约束性更强
2.接口类:与抽象类很相似,表现在接口中定义的方法,必须由引用类实现,但他与抽象类的根本区别在于用途:与不同个体间沟通的规则,你要进宿舍需要有钥匙,这个钥匙就是你与宿舍的接口,你的舍友也有这个接口,所以他也能进入宿舍,你用手机通话,那么手机就是你与他人交流的接口
3.区别和关联:
1.接口是抽象类的变体,接口中所有的方法都是抽象的,而抽象类中可以有非抽象方法,抽象类是声明方法的存在而不去实现它的类
2.接口可以继承,抽象类不行
3.接口定义方法,没有实现的代码,而抽象类可以实现部分方法
4.接口中基本数据类型为static而抽象类不是
if hasattr(Parent, 'x'): print(getattr(Parent, 'x')) setattr(Parent, 'x',3) print(getattr(Parent,'x'))
A,变量不必事先声明 B,变量无须先创建和赋值而直接使用
C,变量无须指定类型 D,可以使用del释放资源
内存管理机制: 引用计数、垃圾回收、内存池
引用计数:引用计数是一种非常高效的内存管理手段,当一个Python对象被引用时其引用计数增加1,
当其不再被一个变量引用时则计数减1,当引用计数等于0时对象被删除。弱引用不会增加引用计数
垃圾回收:
1.引用计数
引用计数也是一种垃圾收集机制,而且也是一种最直观、最简单的垃圾收集技术。当Python的某个对象的引用计数降为0时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾了。比如某个新建对象,它被分配给某个引用,对象的引用计数变为1,如果引用被删除,对象的引用计数为0,那么该对象就可以被垃圾回收。不过如果出现循环引用的话,引用计数机制就不再起有效的作用了。
2.标记清除
https://foofish.net/python-gc.html
调优手段
1.手动垃圾回收
2.调高垃圾回收阈值
3.避免循环引用
内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。
有__del__()
函数的对象间的循环引用是导致内存泄露的主凶。不使用一个对象时使用: del object 来删除一个对象的引用计数就可以有效防止内存泄露问题。
通过Python扩展模块gc 来查看不能回收的对象的详细信息。
可以通过 sys.getrefcount(obj) 来获取对象的引用计数,并根据返回值是否为0来判断是否内存泄露
[表达式 for 变量 in 列表] 或者 [表达式 for 变量 in 列表 if 条件]
read 读取整个文件
readline 读取下一行
readlines 读取整个文件到一个迭代器以供我们遍历
散列函数(英语:Hash function)又称散列算法、哈希函数,是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值(hash values,hash codes,hash sums,或hashes)的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表
函数重载主要是为了解决两个问题。 1。可变参数类型。 2。可变参数个数。
另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。
好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。
那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。
好了,鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。
import datetime class TimeException(Exception): def __init__(self, exception_info): super().__init__() self.info = exception_info def __str__(self): return self.info def timecheck(func): def wrapper(*args, **kwargs): if datetime.datetime.now().year == 2019: func(*args, **kwargs) else: raise TimeException("函数已过时") return wrapper @timecheck def test(name): print("Hello {}, 2019 Happy".format(name)) if __name__ == "__main__": test("backbp")
list(filter(lambda x: x % 2 == 0, range(10)))
1.函数设计要尽量短小
2.函数声明要做到合理、简单、易于使用
3.函数参数设计应该考虑向下兼容
4.一个函数只做一件事情,尽量保证函数语句粒度的一致性
Python的参数传递有:位置参数、默认参数、可变参数、关键字参数。
函数的传值到底是值传递还是引用传递、要分情况:
不可变参数用值传递:像整数和字符串这样的不可变对象,是通过拷贝进行传递的,因为你无论如何都不可能在原处改变不可变对象。
可变参数是引用传递:比如像列表,字典这样的对象是通过引用传递、和C语言里面的用指针传递数组很相似,可变对象能在函数内部改变。
globals() # 返回包含当前作用余全局变量的字典。 global 变量 设置使用全局变量
缺省参数指在调用函数的时候没有传入参数的情况下,调用默认的参数,在调用函数的同时赋值时,所传入的参数会替代默认参数。
*args是不定长参数,它可以表示输入参数是不确定的,可以是任意多个。
**kwargs是关键字参数,赋值的时候是以键值对的方式,参数可以是任意多对在定义函数的时候
不确定会有多少参数会传入时,就可以使用两个参数
带定长参数的装饰器
def new_func(func): def wrappedfun(username, passwd): if username == 'root' and passwd == '123456789': print('通过认证') print('开始执行附加功能') return func() else: print('用户名或密码错误') return return wrappedfun @new_func def origin(): print('开始执行函数') origin('root','123456789')
带不定长参数的装饰器
def new_func(func): def wrappedfun(*parts): if parts: counts = len(parts) print('本系统包含 ', end='') for part in parts: print(part, ' ',end='') print('等', counts, '部分') return func() else: print('用户名或密码错误') return func() return wrappedfun
Python中一切皆对象,函数名是函数在内存中的空间,也是一个对象
在编写代码时只写框架思路,具体实现还未编写就可以用pass进行占位,是程序不报错,不会进行任何操作。
a = 10 b = 20 c = [a] a = 15
答:10对于字符串,数字,传递是相应的值
a, b = b, a
map(lambda x: x * x, [1, 2, 3, 4]) # 使用 lambda # [1, 4, 9, 16] reduce(lambda x, y: x * y, [1, 2, 3, 4]) # 相当于 ((1 * 2) * 3) * 4 # 24
回调函数是把函数的指针(地址)作为参数传递给另一个函数,将整个函数当作一个对象,赋值给调用的函数。
内建类型:布尔类型,数字,字符串,列表,元组,字典,集合
输出字符串'a'的内建方法
[0, 1, 4]
hasattr(object,name)函数:
判断一个对象里面是否有name属性或者name方法,返回bool值,有name属性(方法)返回True,否则返回False。
class function_demo(object): name = 'demo' def run(self): return "hello function" functiondemo = function_demo() res = hasattr(functiondemo, "name") # 判断对象是否有name属性,True res = hasattr(functiondemo, "run") # 判断对象是否有run方法,True res = hasattr(functiondemo, "age") # 判断对象是否有age属性,False print(res)
getattr(object, name[,default])函数:
获取对象object的属性或者方法,如果存在则打印出来,如果不存在,打印默认值,默认值可选。注意:如果返回的是对象的方法,则打印结果是:方法的内存地址,如果需要运行这个方法,可以在后面添加括号().
functiondemo = function_demo() getattr(functiondemo, "name")# 获取name属性,存在就打印出来 --- demo getattr(functiondemo, "run") # 获取run 方法,存在打印出方法的内存地址 getattr(functiondemo, "age") # 获取不存在的属性,报错 getattr(functiondemo, "age", 18)# 获取不存在的属性,返回一个默认值
setattr(object, name, values)函数:
给对象的属性赋值,若属性不存在,先创建再赋值
class function_demo(object): name = "demo" def run(self): return "hello function" functiondemo = function_demo() res = hasattr(functiondemo, "age") # 判断age属性是否存在,False print(res) setattr(functiondemo, "age", 18) # 对age属性进行赋值,无返回值 res1 = hasattr(functiondemo, "age") # 再次判断属性是否存在,True
综合使用
class function_demo(object): name = "demo" def run(self): return "hello function" functiondemo = function_demo() res = hasattr(functiondemo, "addr") # 先判断是否存在 if res: addr = getattr(functiondemo, "addr") print(addr) else: addr = getattr(functiondemo, "addr", setattr(functiondemo, "addr", "北京首都")) print(addr)
reduce(lambda x,y : x*y,range(1,n+1))
lambda 函数是一个可以接收任意多个参数(包括可选参数)并且返回单个表达式值的函数
1.lambda函数比较轻便,即用即仍,很适合需要完成一项功能,但是此功能只在此一处使用,连名字都很随意的情况下
2.匿名函数,一般用来给filter,map这样的函数式编程服务
3.作为回调函数,传递给某些应用,比如消息处理
递归的终止条件一般定义在递归函数内部,在递归调用前要做一个条件判断,根据判断的结果选择是继续调用自身,还是return,,返回终止递归。
终止的条件:判断递归的次数是否达到某一限定值
2.判断运算的结果是否达到某个范围等,根据设计的目的来选择
def multipliers(): return [lambda x: i *x for i in range(4)] print([m(2) for m in multipliers()])
上面代码的输出结果是[6,6,6,6],不是我们想的[0,2,4,6]
你如何修改上面的multipliers的定义产生想要的结果?
上述问题产生的原因是python闭包的延迟绑定。这意味着内部函数被调用时,参数的值在闭包内进行查找。因此,当任何由multipliers()返回的函数被调用时,i的值将在附近的范围进行查找。那时,不管返回的函数是否被调用,for循环已经完成,i被赋予了最终的值3.
def multipliers(): for i in range(4): yield lambda x: i *x
def multipliers(): return [lambda x,i = i: i*x for i in range(4)]
lambda函数是匿名函数,使用lambda函数能创建小型匿名函数,这种函数得名于省略了用def声明函数的标准步骤
设计模式是经过总结,优化的,对我们经常会碰到的一些编程问题的可重用解决方案。一个设计模式并不像一个类或一个库那样能够直接作用于我们的代码,反之,设计模式更为高级,它是一种必须在特定情形下实现的一种方法模板。 常见的是工厂模式和单例模式
#python2 class A(object): __instance = None def __new__(cls,*args,**kwargs): if cls.__instance is None: cls.__instance = objecet.__new__(cls) return cls.__instance else: return cls.__instance
单例模式应用的场景一般发现在以下条件下: 资源共享的情况下,避免由于资源操作时导致的性能或损耗等,如日志文件,应用配置。 控制资源的情况下,方便资源之间的互相通信。如线程池等,1,网站的计数器 2,应用配置 3.多线程池 4数据库配置 数据库连接池 5.应用程序的日志应用...
print([x*x for x in range(1, 11)])
装饰器本质上是一个callable object ,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。
import time from functools import wraps def timeit(func): @wraps(func) def wrapper(*args, **kwargs): start = time.clock() ret = func(*args, **kwargs) end = time.clock() print('used:',end-start) return ret return wrapper @timeit def foo(): print('in foo()'foo())
在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包。
装饰器本质上是一个callable object,它可以在让其他函数在不需要做任何代码的变动的前提下增加额外的功能。装饰器的返回值也是一个函数的对象,它经常用于有切面需求的场景。比如:插入日志,性能测试,事务处理,缓存。权限的校验等场景,有了装饰器就可以抽离出大量的与函数功能本身无关的雷同代码并发并继续使用。 详细参考:https://manjusaka.itscoder.com/2018/02/23/something-about-decorator/
迭代器是遵循迭代协议的对象。用户可以使用 iter() 以从任何序列得到迭代器(如 list, tuple, dictionary, set 等)。另一个方法则是创建一个另一种形式的迭代器 —— generator 。要获取下一个元素,则使用成员函数 next()(Python 2)或函数 next() function (Python 3) 。当没有元素时,则引发 StopIteration 此例外。若要实现自己的迭代器,则只要实现 next()(Python 2)或 __next__
()( Python 3)
生成器(Generator),只是在需要返回数据的时候使用yield语句。每次next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值)
区别: 生成器能做到迭代器能做的所有事,而且因为自动创建iter()和next()方法,生成器显得特别简洁,而且生成器也是高效的,使用生成器表达式取代列表解析可以同时节省内存。除了创建和保存程序状态的自动方法,当发生器终结时,还会自动抛出StopIteration异常。
官方介绍:https://docs.python.org/3/tutorial/classes.html#iterators
X= (i for i in range(10))
X是 generator类型
N =100 print ([[x for x in range(1,100)] [i:i+3] for i in range(0,100,3)])
yield就是保存当前程序执行状态。你用for循环的时候,每次取一个元素的时候就会计算一次。用yield的函数叫generator,和iterator一样,它的好处是不用一次计算所有元素,而是用一次算一次,可以节省很多空间,generator每次计算需要上一次计算结果,所以用yield,否则一return,上次计算结果就没了
不可变对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。
可变对象,该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上其所指的值直接发生改变,并没有发生复制行为,也没有开辟出新的地址,通俗点说就是原地改变。
Pyhton中,数值类型(int 和float),字符串str、元祖tuple都是不可变类型。而列表list、字典dict、集合set是可变类型
魔法方法就是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被Python所调用,你可以定义自己想要的行为,而这一切都是自动发生的,它们经常是两个下划线包围来命名的(比如__init___
,__len__
),Python的魔法方法是非常强大的所以了解其使用方法也变得尤为重要!
__init__
构造器,当一个实例被创建的时候初始化的方法,但是它并不是实例化调用的第一个方法。
__new__
才是实例化对象调用的第一个方法,它只取下cls参数,并把其他参数传给__init___
.
___new__
很少使用,但是也有它适合的场景,尤其是当类继承自一个像元祖或者字符串这样不经常改变的类型的时候。
__call__
让一个类的实例像函数一样被调用
__getitem__
定义获取容器中指定元素的行为,相当于self[key]
__getattr__
定义当用户试图访问一个不存在属性的时候的行为。
__setattr__
定义当一个属性被设置的时候的行为
__getattribute___
定义当一个属性被访问的时候的行为
将对象私有化,通过共有方法提供一个读取数据的接口
class person: def __init__(self, x): self.__age = 10 def age(self): return self.__age t = person(22) # t.__age =100 print(t.age())
最好的方法
class MyCls(object): __weight = 50 @property def weight(self): return self.__weight
面向对象是相当于面向过程而言的,面向过程语言是一种基于功能分析的,以算法为中心的程序设计方法,而面向对象是一种基于结构分析的,以数据为中心的程序设计思想。在面向对象语言中有一个很重要的东西,叫做类。面向对象有三大特性:封装、继承、多态。
进程:程序运行在操作系统上的一个实例,就称之为进程。进程需要相应的系统资源:内存、时间片、pid。 创建进程: 首先要导入multiprocessing中的Process: 创建一个Process对象; 创建Process对象时,可以传递参数;
p = Process(target=XXX,args=(tuple,),kwargs={key:value}) target = XXX 指定的任务函数,不用加(), args=(tuple,)kwargs={key:value}给任务函数传递的参数
使用start()启动进程 结束进程 给子进程指定函数传递参数Demo
import os from mulitprocessing import Process import time def pro_func(name,age,**kwargs): for i in range(5): print("子进程正在运行中,name=%s,age=%d,pid=%d"%(name,age,os.getpid())) print(kwargs) time.sleep(0.2) if __name__ =="__main__": #创建Process对象 p = Process(target=pro_func,args=('小明',18),kwargs={'m':20}) #启动进程 p.start() time.sleep(1) #1秒钟之后,立刻结束子进程 p.terminate() p.join()
注意:进程间不共享全局变量
进程之间的通信-Queue
在初始化Queue()对象时(例如q=Queue(),若在括号中没有指定最大可接受的消息数量,获数量为负值时,那么就代表可接受的消息数量没有上限一直到内存尽头)
Queue.qsize():返回当前队列包含的消息数量
Queue.empty():如果队列为空,返回True,反之False
Queue.full():如果队列满了,返回True,反之False
Queue.get([block[,timeout]]):获取队列中的一条消息,然后将其从队列中移除,
block默认值为True。
如果block使用默认值,且没有设置timeout(单位秒),消息队列如果为空,此时程序将被阻塞(停在读中状态),直到消息队列读到消息为止,如果设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出“Queue.Empty"异常:
Queue.get_nowait()相当于Queue.get(False)
Queue.put(item,[block[,timeout]]):将item消息写入队列,block默认值为True; 如果block使用默认值,且没有设置timeout(单位秒),消息队列如果已经没有空间可写入,此时程序将被阻塞(停在写入状态),直到从消息队列腾出空间为止,如果设置了timeout,则会等待timeout秒,若还没空间,则抛出”Queue.Full"异常 如果block值为False,消息队列如果没有空间可写入,则会立刻抛出"Queue.Full"异常; Queue.put_nowait(item):相当Queue.put(item,False)
进程间通信Demo:
from multiprocessing import Process.Queue import os,time,random #写数据进程执行的代码: def write(q): for value in ['A','B','C']: print("Put %s to queue...",%value) q.put(value) time.sleep(random.random()) #读数据进程执行的代码 def read(q): while True: if not q.empty(): value = q.get(True) print("Get %s from queue.",%value) time.sleep(random.random()) else: break if __name__=='__main__': #父进程创建Queue,并传给各个子进程 q = Queue() pw = Process(target=write,args=(q,)) pr = Process(target=read,args=(q,)) #启动子进程pw ,写入: pw.start() #等待pw结束 pw.join() #启动子进程pr,读取: pr.start() pr.join() #pr 进程里是死循环,无法等待其结束,只能强行终止: print('') print('所有数据都写入并且读完')
进程池Pool
#coding:utf-8 from multiprocessing import Pool import os,time,random def worker(msg): t_start = time.time() print("%s 开始执行,进程号为%d"%(msg,os.getpid())) # random.random()随机生成0-1之间的浮点数 time.sleep(random.random()*2) t_stop = time.time() print(msg,"执行完毕,耗时%0.2f”%(t_stop-t_start)) po = Pool(3)#定义一个进程池,最大进程数3 for i in range(0,10): po.apply_async(worker,(i,)) print("---start----") po.close() po.join() print("----end----")
进程池中使用Queue
如果要使用Pool创建进程,就需要使用multiprocessing.Manager()中的Queue(),而不是multiprocessing.Queue(),否则会得到如下的错误信息:
RuntimeError: Queue objects should only be shared between processs through inheritance
from multiprocessing import Manager,Pool import os,time,random def reader(q): print("reader 启动(%s),父进程为(%s)"%(os.getpid(),os.getpid())) for i in range(q.qsize()): print("reader 从Queue获取到消息:%s"%q.get(True)) def writer(q): print("writer 启动(%s),父进程为(%s)"%(os.getpid(),os.getpid())) for i ini "itcast": q.put(i) if __name__ == "__main__": print("(%s)start"%os.getpid()) q = Manager().Queue()#使用Manager中的Queue po = Pool() po.apply_async(wrtier,(q,)) time.sleep(1) po.apply_async(reader,(q,)) po.close() po.join() print("(%s)End"%os.getpid())
这个问题被问的概念相当之大, 进程:一个运行的程序(代码)就是一个进程,没有运行的代码叫程序,进程是系统资源分配的最小单位,进程拥有自己独立的内存空间,所有进程间数据不共享,开销大。
线程: cpu调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程存在,一个进程至少有一个线程,叫主线程,而多个线程共享内存(数据共享,共享全局变量),从而极大地提高了程序的运行效率。
协程: 是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操中栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
异步的使用场景:
1、 不涉及共享资源,获对共享资源只读,即非互斥操作
2、 没有时序上的严格关系
3、 不需要原子操作,或可以通过其他方式控制原子性
4、 常用于IO操作等耗时操作,因为比较影响客户体验和使用性能
5、 不影响主线程逻辑
import threading import time class MyThread(threading.Thread): def run(self): global num time.sleep(1) if mutex.acquire(1): num +=1 msg = self.name + 'set num to ' +str(num) print msg mutex.release() num = 0 mutex = threading.Lock() def test(): for i in range(5): t = MyThread() t.start() if __name__=="__main__": test()
线程是非独立的,同一个进程里线程是数据共享的,当各个线程访问数据资源时会出现竞争状态即:数据几乎同步会被多个线程占用,造成数据混乱,即所谓的线程不安全
那么怎么解决多线程竞争问题?---锁
锁的好处: 确保了某段关键代码(共享数据资源)只能由一个线程从头到尾完整地执行能解决多线程资源竞争下的原子操作问题。
锁的坏处: 阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了
锁的致命问题: 死锁
一、 setDaemon(False) 当一个进程启动之后,会默认产生一个主线程,因为线程是程序执行的最小单位,当设置多线程时,主线程会创建多个子线程,在Python中,默认情况下就是setDaemon(False),主线程执行完自己的任务以后,就退出了,此时子线程会继续执行自己的任务,直到自己的任务结束。
例子
import threading import time def thread(): time.sleep(2) print('---子线程结束---') def main(): t1 = threading.Thread(target=thread) t1.start() print('---主线程--结束') if __name__ =='__main__': main() #执行结果 ---主线程--结束 ---子线程结束---
二、 setDaemon(True) 当我们使用setDaemon(True)时,这是子线程为守护线程,主线程一旦执行结束,则全部子线程被强制终止
例子
import threading import time def thread(): time.sleep(2) print(’---子线程结束---') def main(): t1 = threading.Thread(target=thread) t1.setDaemon(True)#设置子线程守护主线程 t1.start() print('---主线程结束---') if __name__ =='__main__': main() #执行结果 ---主线程结束--- #只有主线程结束,子线程来不及执行就被强制结束
三、 join(线程同步) join 所完成的工作就是线程同步,即主线程任务结束以后,进入堵塞状态,一直等待所有的子线程结束以后,主线程再终止。
当设置守护线程时,含义是主线程对于子线程等待timeout的时间将会杀死该子线程,最后退出程序,所以说,如果有10个子线程,全部的等待时间就是每个timeout的累加和,简单的来说,就是给每个子线程一个timeou的时间,让他去执行,时间一到,不管任务有没有完成,直接杀死。
没有设置守护线程时,主线程将会等待timeout的累加和这样的一段时间,时间一到,主线程结束,但是并没有杀死子线程,子线程依然可以继续执行,直到子线程全部结束,程序退出。
例子
import threading import time def thread(): time.sleep(2) print('---子线程结束---') def main(): t1 = threading.Thread(target=thread) t1.setDaemon(True) t1.start() t1.join(timeout=1)#1 线程同步,主线程堵塞1s 然后主线程结束,子线程继续执行 #2 如果不设置timeout参数就等子线程结束主线程再结束 #3 如果设置了setDaemon=True和timeout=1主线程等待1s后会强制杀死子线程,然后主线程结束 print('---主线程结束---') if __name__=='__main___': main()
锁(Lock)是python提供的对线程控制的对象。有互斥锁,可重入锁,死锁。
若干子线程在系统资源竞争时,都在等待对方对某部分资源解除占用状态,结果是谁也不愿先解锁,互相干等着,程序无法执行下去,这就是死锁。
GIL锁 全局解释器锁
作用: 限制多线程同时执行,保证同一时间只有一个线程执行,所以cython里的多线程其实是伪多线程!
所以python里常常使用协程技术来代替多线程,协程是一种更轻量级的线程。
进程和线程的切换时由系统决定,而协程由我们程序员自己决定,而模块gevent下切换是遇到了耗时操作时才会切换
三者的关系:进程里有线程,线程里有协程。
怎么避免重读?
创建一个已访问数据列表,用于存储已经访问过的数据,并加上互斥锁,在多线程访问数据的时候先查看数据是否在已访问的列表中,若已存在就直接跳过。
每个对象都对应于一个可称为’互斥锁‘的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
同一进程中的多线程之间是共享系统资源的,多个线程同时对一个对象进行操作,一个线程操作尚未结束,另一线程已经对其进行操作,导致最终结果出现错误,此时需要对被操作对象添加互斥锁,保证每个线程对该对象的操作都得到正确的结果。
同步: 多个任务之间有先后顺序执行,一个执行完下个才能执行。
异步: 多个任务之间没有先后顺序,可以同时执行,有时候一个任务可能要在必要的时候获取另一个同时执行的任务的结果,这个就叫回调!
阻塞: 如果卡住了调用者,调用者不能继续往下执行,就是说调用者阻塞了。
非阻塞: 如果不会卡住,可以继续执行,就是说非阻塞的。
同步异步相对于多任务而言,阻塞非阻塞相对于代码执行而言。
孤儿进程: 父进程退出,子进程还在运行的这些子进程都是孤儿进程,孤儿进程将被init 进程(进程号为1)所收养,并由init 进程对他们完成状态收集工作。
僵尸进程: 进程使用fork 创建子进程,如果子进程退出,而父进程并没有调用wait 获waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中的这些进程是僵尸进程。
避免僵尸进程的方法:
1.fork 两次用孙子进程去完成子进程的任务
2.用wait()函数使父进程阻塞
3.使用信号量,在signal handler 中调用waitpid,这样父进程不用阻塞
多进程适合在CPU密集操作(cpu操作指令比较多,如位多的的浮点运算)。
多线程适合在IO密性型操作(读写数据操作比多的的,比如爬虫)
线程是并发,进程是并行;
进程之间互相独立,是系统分配资源的最小单位,同一个线程中的所有线程共享资源。
并行: 同一时刻多个任务同时在运行
不会在同一时刻同时运行,存在交替执行的情况。
实现并行的库有: multiprocessing
实现并发的库有: threading
程序需要执行较多的读写、请求和回复任务的需要大量的IO操作,IO密集型操作使用并发更好。
CPU运算量大的程序,使用并行会更好
IO密集型: 系统运行,大部分的状况是CPU在等 I/O(硬盘/内存)的读/写
CPU密集型: 大部分时间用来做计算,逻辑判断等CPU动作的程序称之CPU密集型。
asyncio这个库就是使用python的yield这个可以打断保存当前函数的上下文的机制, 封装好了selector 摆脱掉了复杂的回调关系
浏览器发送的请求被Nginx监听到,Nginx根据请求的URL的PATH或者后缀把请求静态资源的分发到静态资源的目录,别的请求根据配置好的转发到相应端口。 实现了WSGI的程序会监听某个端口,监听到Nginx转发过来的请求接收后(一般用socket的recv来接收HTTP的报文)以后把请求的报文封装成environ
的字典对象,然后再提供一个start_response
的方法。把这两个对象当成参数传入某个方法比如wsgi_app(environ, start_response)
或者实现了__call__(self, environ, start_response)
方法的某个实例。这个实例再调用start_response
返回给实现了WSGI的中间件,再由中间件返回给Nginx。