教程
信息差
资源
软件工具
技术笔记
AIGC
视频
Search
1
使用AI实现高精度钢琴曲转谱Piano Transcription简明使用教程
37,794 阅读
2
使用ESP8266Wifi模块制作Wifi杀手
37,467 阅读
3
unravel 让图片唱歌详细教程 Real Time Image Animation 项目
27,386 阅读
4
佳能相机刷Magic Lantern魔灯固件
23,501 阅读
5
战地3 正版账号免费分享!
16,213 阅读
教程
信息差
资源
软件工具
技术笔记
AIGC
视频
Search
标签搜索
python
前端
环境搭建
空镜素材
Ubuntu
markdown
神器
黑苹果
编码
技巧
Git
数据库
开发
下载工具
Youtube
CDN
PDF
OST
电影原声带
音乐
易小灯塔
累计撰写
176
篇文章
累计收到
44
条评论
首页
栏目
教程
信息差
资源
软件工具
技术笔记
AIGC
视频
页面
搜索到
30
篇与
的结果
2017-08-11
Python socket套接字之TCP编程
TCP简介TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP通信需要经过创建连接、数据传送、终止连接三个步骤。TCP通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据,类似于生活中打电话。TCP特点1)TCP采用发送应答机制TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功。2)超时重传 发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。3)错误校验TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。4)流量控制和阻塞管理流量控制用来避免主机发送得过快而使接收方来不及完全收下。TCP与UDP的不同点 面向连接(确认有创建三方交握,连接已创建才作传输。) 有序数据传输 重发丢失的数据包 舍弃重复的数据包 无差错的数据传输 阻塞/流量控制 TCP通信模型(C/S模型)基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: TCP编程:客户端对于 TCP 客户端编程流程,有点类似于打电话过程: 找个可以通话的手机(socket() ) 拨通对方号码并确定对方是自己要找的人( connect() ) 主动聊天( send() ) 或者,接收对方的回话( recv()) 通信结束后,双方说再见挂电话(close() ) 示例代码:import socket #1、创建TCP套接字 tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #2、链接服务器 tcp.connect(("172.16.1.52", 8888)) #3、发送数据 tcp.send("are u ok?".encode("utf-8")) #4、接收数据,最大一次接收1024个字节 recvData = tcp.recv(1024) print("recvData = ", recvData.decode("utf-8")) #5、关闭套接字 tcp.close() TCP编程:服务器对于 TCP 服务器编程流程,有点类似于接电话过程: 找个可以通话的手机(socket() ) 插上电话卡固定一个号码( bind() ) 职责为被动接听,给手机设置一个铃声来监听是否有来电( listen() ) 有来电,确定双方的关系后,才真正接通不挂电话( accept() ) 接听对方的诉说( recv() ) 适当给些回话( send() ) 通信结束后,双方说再见挂电话( close() ) 示例代码:import socket #1、创建TCP套接字 tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #2、绑定本地网络信息 tcp.bind(('', 8888)) #3、使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的链接了 tcp.listen(128) #4、如果有新的客户端来链接服务器,那么就产生一个新的套接字专门为这个客户端服务 # clientSocket用来为这个客户端服务 # tcp专门等待其他新客户端的链接 clientSocket, clientAddr = tcp.accept() #5、接收对方发送过来的数据,一次最大接收1024个字节 recvData = clientSocket.recv(1024) print(clientAddr, " >>>>>>> ", recvData.decode("utf-8")) #6、发送一些数据到客户端 clientSocket.send("thanks".encode("utf-8")) #7、关闭为这个客户端服务的套接字,只要关闭了,就意味着为不能再为这个客户端服务了,如果还需要服务,只能再次重新连接 clientSocket.close() 使用TCP注意点 tcp服务器一般情况下都需要绑定,否则客户端找不到这个服务器 tcp客户端一般不绑定,因为是主动链接服务器,所以只要确定好服务器的ip、port等信息就好,本地客户端可以随机 tcp服务器中通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务器时必须要做的 当客户端需要链接服务器时,就需要使用connect进行链接,udp是不需要链接的而是直接发送,但是tcp必须先链接,只有链接成功才能通信 当一个tcp客户端连接服务器时,服务器端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务 listen后的套接字是被动套接字,用来接收新的客户端的链接请求的,而accept返回的新套接字是标记这个新客户端的 关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够链接服务器,但是之前已经链接成功的客户端正常通信 关闭accept返回的套接字意味着这个客户端已经服务完毕 当客户端的套接字调用close后,服务器端会recv解堵塞,并且返回的长度为0,因此服务器可以通过返回数据的长度来区别客户端是否已经下线
2017年08月11日
2,126 阅读
0 评论
0 点赞
2017-08-10
TCP的三次握手和四次挥手
三次握手在 TCP/IP 协议中,TCP 协议提供可靠的连接服务,采用三次握手建立一个连接。通过这样的三次握手,客户端与服务端建立起可靠的双工的连接,开始传送数据。 三次握手的最主要目的是保证连接是双工的,可靠更多的是通过重传机制来保证的。但是为什么一定要进行三次握手来保证连接是双工的呢,一次不行么?两次不行么?举一个现实中例子来解释三次握手。和女朋友异地恋一年多,为了保持感情我提议每天晚上视频聊天一次。有时候聊天的过程中,我的网络或者她的网络可能会不好,视频就会卡住,听不到对方的声音,过一会儿之后才会恢复。中间双方可能就要不断的确认网络是否恢复,但是有时候会:她:“你可以听到了吗?” 我:“可以了,你呢?” 她:“喂喂,你可以听到了吗?” 我:“可以了,我可以听到了,你呢?” 她:“你可以听到了吗?” ..... 这种情况很蛋疼,那么怎样才能找一个简单的办法,让两个人都确认自己可以听到对方的声音,对方也可以听到自己的声音呢?于是有了如下对话:我:1+1等于几? 她:2,2+2等于几? 我:4 四次挥手由于 TCP 连接是全双工的,因此每个方向都必须单独进行关闭。这好比,我们打电话(全双工),正常的情况下(出于礼貌),通话的双方都要说再见后才能挂电话,保证通信双方都把话说完了才挂电话。套接字端口复用如果客户端和服务器已经连接成功的前提下,通常的情况下,先关闭客户端,再关闭服务器,如果是先关闭服务器,立马启动服务器是,服务器绑定的端口不会立马释放(如下图),要过 1~2 分钟左右才会释放。# 套接字端口复用 tcp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定信息 tcp.bind(("", 8888))
2017年08月10日
3,079 阅读
0 评论
0 点赞
2017-08-07
Python的全局解释器锁GIL
Python语言和GIL没有半毛钱关系。仅仅是由于历史原因在Cpython虚拟机(解释器),难以移除GIL。GIL:全局解释器锁。每个线程在执行的过程都需要先获取GIL,保证同一时刻只有一个线程可以执行代码。线程释放GIL锁的情况: 在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL Python 3.x使用计时器(执行时间达到阈值后,当前线程释放GIL)或Python 2.x,tickets计数达到100 Python使用多进程是可以利用多核的CPU资源的。 多线程爬取比单线程性能有提升,因为遇到IO阻塞会自动释放GIL锁 结论:1. 在 处理像科学计算 这类需要持续使用cpu的任务的时候 单线程会比多线程快 2. 在 处理像IO操作等可能引起阻塞的这类任务的时候 多线程会比单线程快当需要执行计算密集型的程序时,可以选择:1. 换解释器 2. 关键地方用C语言是实现 3. .换多进程+协程方案
2017年08月07日
1,563 阅读
0 评论
0 点赞
2017-08-07
Python的其他高阶函数
map()函数map()是 Python 内置的高阶函数,它接收一个函数 f 和一个list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回。def f(x): return x * x print map(f,[1,2,3,4,5,6,7]) #list里的每个元素都会走一遍f(x)方法123 结果将会是:[1, 4, 9, 10, 25, 36, 49] reduce()函数reduce()函数也是Python内置的一个高阶函数。reduce()函数接收的参数和 map()类似,一个函数 f,一个list,但行为和 map()不同,reduce()传入的函数 f 必须接收两个参数,reduce()对list的每个元素反复调用函数f,并返回最终结果值。def f(x ,y ): return x * y print reduce(f,[1,2,3,4]) #1*2*3*4=24 # 给初始值 def f(a,b): return a+ b print reduce(f,[1,2,3,4],10) #1+2+3+4+10.这里的第三个参数是做为初始值的。12345678 filter()函数filter()函数是 Python 内置的另一个有用的高阶函数,filter()函数接收一个函数 f 和一个list,这个函数 f 的作用是对每个元素进行判断,返回 True或 False,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。def is_odd(x): return x%2==1 print filter(is_odd,[1,2,3,4,5,6,7]) # [1, 3, 5, 7]123 sorted()函数sorted() 函数对所有可迭代的对象进行排序操作。sort 与 sorted 区别: sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。 list 的 sort 方法返回的是对已经存在的列表进行操作,而内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。语法 sorted 语法:sorted(iterable[, cmp[, key[, reverse]]])1 参数说明: iterable -可迭代对象。 cmp -比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。 key -主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。 reverse -排序规则,reverse = True 降序 , reverse = False 升序(默认)。 返回值返回重新排序的列表。>>>a = [5,7,6,3,4,1,2] >>> b = sorted(a) # 保留原列表 >>> a [5, 7, 6, 3, 4, 1, 2] >>> b [1, 2, 3, 4, 5, 6, 7] >>> L=[('b',2),('a',1),('c',3),('d',4)] >>> sorted(L, cmp=lambda x,y:cmp(x[1],y[1])) # 利用cmp函数 [('a', 1), ('b', 2), ('c', 3), ('d', 4)] >>> sorted(L, key=lambda x:x[1]) # 利用key [('a', 1), ('b', 2), ('c', 3), ('d', 4)] >>> students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)] >>> sorted(students, key=lambda s: s[2]) # 按年龄排序 [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] >>> sorted(students, key=lambda s: s[2], reverse=True) # 按降序 [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)] >>>
2017年08月07日
640 阅读
0 评论
0 点赞
2017-08-07
Python的匿名函数
为什么Lisp语言如此先进?如果我们把流行的编程语言,以这样的顺序排列:Java、Perl、Python、Ruby。你会发现,排在越后面的语言,越像Lisp。Python模仿Lisp,甚至把许多Lisp黑客认为属于设计错误的功能,也一起模仿了。至于Ruby,如果回到1975年,你声称它是一种Lisp方言,没有人会反对。编程语言现在的发展,不过刚刚赶上1958年Lisp语言的水平。-- 《黑客与画家》Python匿名函数应该就是借鉴的Lisp语言的特点 使用Python写一些脚本时,使用lambda可以省去定义函数的过程,让代码更加精简。 对于一些抽象的,不会被别的地方再重复使用的函数,有时候函数起个名字也是个难题,使用lambda不需要考虑命名的问题 使用lambda在某些时候然后代码更容易理解 python 使用 lambda 来创建匿名函数。lambda这个名称来自于LISP,而LISP则是从lambda calculus(一种符号逻辑形式)取这个名称的。 在Python中,lambda作为一个关键字,作为引入表达式的语法。想比较def函数,lambda是单一的表达式,而不是语句块!所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。 lambda 只是一个表达式,函数体比 def 简单很多。 lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。 lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。 语法 lambda 函数的语法只包含一个语句,如下:lambda [arg1 [,arg2,.....argn]]:expression # lambda 参数列表:return [表达式] 变量 # 由于lambda返回的是函数对象(构建的是一个函数对象),所以需要定义一个变量去接收1234 实例一# 可写函数说明 sum = lambda arg1, arg2: arg1 + arg2; # 调用sum函数 print ("相加后的值为 : ", sum( 10, 20 )) # 相加后的值为 : 30 print ("相加后的值为 : ", sum( 20, 20 )) # 相加后的值为 : 40 1234567 实例二对字典排序infors = [{"name":"wang","age":10},{"name":"insmoin","age":20},{"name":"banzhang","age":10}] infors.sort(key=lambda x:x['age']) #根据age对字典排序 print(infors) # 排序结果 [{'name': 'wang', 'age': 10}, {'name': 'insmoin', 'age': 10}, {'name': 'xiaoming', 'age': 20}]1234567 实例三把lambda当一个变量def test(a,b,func): result = func(a,b) return result num = test(11,22,lambda x,y:x+y) print(num)
2017年08月07日
577 阅读
0 评论
0 点赞
2017-08-07
Python装饰器的理解
Python装饰器的理解 装饰器, 字面理解就是额外的装饰.装饰器的强大在于它能够在不修改原有业务逻辑的情况下对代码进行扩展,权限校验、用户认证、日志记录、性能测试、事务处理、缓存等都是装饰器的绝佳应用场景,它能够最大程度地对代码进行复用。函数作为返回值在Python中,一切皆为对象,函数也不例外,它可以像整数一样作为其它函数的返回值,例如:def foo(): return 1 def bar(): return foo print(bar()) # <function foo at 0x10a2f4140> print(bar()()) # 1 # 等价于 print(foo()) # 1 调用函数 bar() 的返回值是一个函数对象 ,因为返回值是函数,所以我们可以继续对返回值进行调用(记住:调用函数就是在函数名后面加())调用bar()()相当于调用 foo(),因为 变量 foo 指向的对象与 bar() 的返回值是同一个对象。函数作为参数函数还可以像整数一样作为函数的参数,例如:def foo(num): return num + 1 def bar(fun): return fun(3) value = bar(foo) print(value) # 4 函数 bar 接收一个参数,这个参数是一个可被调用的函数对象,把函数 foo 传递到 bar 中去时,foo 和 fun 两个变量名指向的都是同一个函数对象,所以调用 fun(3) 相当于调用 foo(3)。函数嵌套函数不仅可以作为参数和返回值,函数还可以定义在另一个函数中,作为嵌套函数存在,例如:def outer(): x = 1 def inner(): print(x) inner() outer() # 1 inner做为嵌套函数,它可以访问外部函数的变量,调用 outer 函数时,发生了3件事: 给 变量 x 赋值为1 定义嵌套函数 inner,此时并不会执行 inner 中的代码,因为该函数还没被调用,直到第3步 调用 inner 函数,执行 inner 中的代码逻辑。 闭包再来看一个例子:def outer(x): def inner(): print(x) return inner closure = outer(1) closure() # 1 同样是嵌套函数,只是稍改动一下,把局部变量 x 作为参数了传递进来,嵌套函数不再直接在函数里被调用,而是作为返回值返回,这里的 closure就是一个闭包,本质上它还是函数,闭包是引用了自由变量(x)的函数(inner)。装饰器继续往下看:def foo(): print("foo") 上面这个函数这可能是史上最简单的业务代码了,虽然没什么用,但是能说明问题就行。现在,有一个新的需求,需要在执行该函数时加上日志:def foo(): print("记录日志开始") print("foo") print("记录日志结束") 功能实现,唯一的问题就是它需要侵入到原来的代码里面,把日志逻辑加上去,如果还有好几十个这样的函数要加日志,也必须这样做,显然,这样的代码很Low。那么有没有可能在不修改业务代码的提前下,实现日志功能呢?答案就是装饰器。def outer(func): def inner(): print("记录日志开始") func() # 业务函数 print("记录日志结束") return inner def foo(): print("foo") foo = outer(foo) foo() 没有修改 foo 函数里面的任何逻辑,只是给 foo 变量重新赋值了,指向了一个新的函数对象。最后调用 foo(),不仅能打印日志,业务逻辑也执行完了。现在来分析一下它的执行流程。这里的 outer 函数其实就是一个装饰器,装饰器是一个带有函数作为参数并返回一个新函数的闭包,本质上装饰器也是函数。outer 函数的返回值是 inner 函数,在 inner 函数中,除了执行日志操作,还有业务代码,该函数重新赋值给 foo 变量后,调用 foo() 就相当于调用 inner()另外,Python为装饰器提供了语法糖 @,它用在函数的定义处:@outer def foo(): print("foo") foo() 这样就省去了手动给foo重新赋值的步骤。总结,python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
2017年08月07日
783 阅读
0 评论
0 点赞
2017-08-07
Python2 编码问题
编解码 str--(decode)--unicode unicode--(encode)--str print 时会自动将unicode-->strPython2中默认的字符编码是ASCII码,也就是说Python在处理数据时,只要数据没有指定它的编码类型,Python默认将其当做ASCII码来进行处理。这个问题最直接的表现在当我们编写的python文件中包含有中文字符时,在运行时会提示出错python2中中文书写问题 按照u"中文"的方式书写,打印输出时可以指定encode方式,例如u"中文".encode("gbk"),u"中文".encode("utf-8") 如果是从变量获取的值,使用unicode(a) import sys info = raw_input(u'请输入:'.encode(sys.stdin.encoding).decode(sys.stdin.encoding or locale.getpreferredencoding(True))) 这是块级代码块
2017年08月07日
875 阅读
0 评论
0 点赞
2017-08-07
Pyhton 正则表达式 01
正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本许多程序设计语言都支持利用正则表达式进行字符串操作。Python也不例外.正则表达式是对字符串(包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”))操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。正则表达式是一种文本模式,模式描述在搜索文本时要匹配的一个或多个字符串在 Python 中,我们可以使用内置的 re 模块来使用正则表达式。有一点需要特别注意的是,正则表达式使用对特殊字符进行转义,所以如果我们要使用原始字符串,只需加一个 r 前缀,示例:r'chuanzhiboke\t\.\tpython' re模块的使用过程# 导入re模块 import re # 使用match方法进行匹配操作 result = re.match(正则表达式, 要匹配的字符串) # 如果上一步匹配到数据的话,可以使用group方法来提取数据 result.group() 使用示例匹配以insmoin开头的语句: import re result = re.match(r"insmoin","insmoin.com") #能够匹配出以insmoin开头的字符串 result.group() 字符功能 .匹配任意1个字符(除了\n)[ ]匹配[ ]中列举的字符\d匹配数字,即0-9\D匹配非数字,即不是数字\s匹配空白,即空格,tab键\S匹配非空白\w匹配单词字符,即a-z、A-Z、0-9、_\W匹配非单词字符 字符功能 *匹配前一个字符出现0次或者无限次,即可有可无+匹配前一个字符出现1次或者无限次,即至少有1次?匹配前一个字符出现1次或者0次,即要么有1次,要么没有{m}匹配前一个字符出现m次{m, n}匹配前一个字符出现从m到n次 字符功能 ^匹配字符串开头$匹配字符串结尾 字符功能 |匹配左右任意一个表达式(ab)将括号中字符作为一个分组|num引用分组num匹配到的字符串(?P)分组起别名(?P=name)引用别名为name分组匹配到的字符串 匹配出163、126、qq邮箱示例代码:import re ret = re.match("\w{4,20}@163\.com", "test@163.com") print(ret.group()) # test@163.com ret = re.match("\w{4,20}@(163|126|qq)\.com", "test@126.com") print(ret.group()) # test@126.com ret = re.match("\w{4,20}@(163|126|qq)\.com", "test@qq.com") print(ret.group()) # test@qq.com ret = re.match("\w{4,20}@(163|126|qq)\.com", "test@gmail.com") if ret: print(ret.group()) else: print("不是163、126、qq邮箱") # 不是163、126、qq邮箱
2017年08月07日
776 阅读
0 评论
0 点赞
2017-08-07
Python的闭包
作用域作用域是程序运行时变量可被访问的范围,定义在函数内的变量是局部变量,局部变量的作用范围只能是函数内部范围内,它不能在函数外引用。定义在模块最外层的变量是全局变量,它是全局范围内可见的,当然在函数里面也可以读取到全局变量的。例如:num = 10 # 全局作用域变量 def foo(): print(num) # 10 而在函数外部则不可以访问局部变量。例如:def foo(): num = 10 print(num) # NameError: name 'num' is not defined 嵌套函数函数不仅可以定义在模块的最外层,还可以定义在另外一个函数的内部,像这种定义在函数里面的函数称之为嵌套函数(nested function)例如:def print_msg(): # print_msg 是外围函数 msg = "zen of python" def printer(): # printer是嵌套函数 print(msg) printer() # 输出 zen of python print_msg() 对于嵌套函数,它可以访问到其外层作用域中声明的非局部(non-local)变量,比如代码示例中的变量 msg 可以被嵌套函数 printer 正常访问。那么有没有一种可能即使脱离了函数本身的作用范围,局部变量还可以被访问得到呢?答案是闭包什么是闭包函数身为第一类对象,它可以作为函数的返回值返回,现在我们来考虑如下的例子:def print_msg(): # print_msg 是外围函数 msg = "zen of python" def printer(): # printer 是嵌套函数 print(msg) return printer another = print_msg() # 输出 zen of python another() 这段代码和前面例子的效果完全一样,同样输出 "zen of python"。不同的地方在于内部函数 printer 直接作为返回值返回了。一般情况下,函数中的局部变量仅在函数的执行期间可用,一旦 print_msg() 执行过后,我们会认为 msg变量将不再可用。然而,在这里我们发现 print_msg 执行完之后,在调用 another 的时候 msg 变量的值正常输出了,这就是闭包的作用,闭包使得局部变量在函数外被访问成为可能。看完这个例子,我们再来定义闭包,维基百科上的解释是:在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。这里的 another 就是一个闭包,闭包本质上是一个函数,它有两部分组成,printer 函数和变量 msg。闭包使得这些变量的值始终保存在内存中。闭包,顾名思义,就是一个封闭的包裹,里面包裹着自由变量,就像在类里面定义的属性值一样,自由变量的可见范围随同包裹,哪里可以访问到这个包裹,哪里就可以访问到这个自由变量。为什么要使用闭包闭包避免了使用全局变量,此外,闭包允许将函数与其所操作的某些数据(环境)关连起来。这一点与面向对象编程是非常类似的,在面对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联。一般来说,当对象中只有一个方法时,这时使用闭包是更好的选择。来看一个例子:def adder(x): def wrapper(y): return x + y return wrapper adder5 = adder(5) # 输出 15 adder5(10) # 输出 11 adder5(6) 这比用类来实现更优雅,此外装饰器也是基于闭包的一中应用场景。所有函数都有一个 __closure__属性,如果这个函数是一个闭包的话,那么它返回的是一个由 cell 对象 组成的元组对象。cell 对象的cell_contents 属性就是闭包中的自由变量。>>> adder.__closure__ >>> adder5.__closure__ (<cell at 0x103075910: int object at 0x7fd251604518>,) >>> adder5.__closure__[0].cell_contents 5 这解释了为什么局部变量脱离函数之后,还可以在函数之外被访问的原因的,因为它存储在了闭包的 cell_contents中了。
2017年08月07日
1,619 阅读
0 评论
0 点赞
2017-08-07
Queue队列中join()与task_done()的关系
理解Queue队列中join()与task_done()的关系在网上大多关于join()与task_done()的结束原话是这样的:Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号Queue.join() 实际上意味着等到队列为空,再执行别的操作但是可能很多人还是不太理解,这里以我自己的理解来阐述这两者的关联。理解如果线程里每从队列里取一次,但没有执行task_done(),则join无法判断队列到底有没有结束,在最后执行个join()是等不到结果的,会一直挂起。可以理解为,每task_done一次 就从队列里删掉一个元素,这样在最后join的时候根据队列长度是否为零来判断队列是否结束,从而执行主线程。下面看个自己写的例子:下面这个例子,会在join()的地方无限挂起,因为join在等队列清空,但是由于没有task_done,它认为队列还没有清空,还在一直等。'''threading test''' import threading import queue from time import sleep #之所以为什么要用线程,因为线程可以start后继续执行后面的主线程,可以put数据,如果不是线程直接在get阻塞。 class Mythread(threading.Thread): def __init__(self,que): threading.Thread.__init__(self) self.queue = que def run(self): while True: sleep(1) if self.queue.empty(): #判断放到get前面,这样可以,否则队列最后一个取完后就空了,直接break,走不到print break item = self.queue.get() print(item,'!') #self.queue.task_done() return que = queue.Queue() tasks = [Mythread(que) for x in range(1)] for x in range(10): que.put(x) #快速生产 for x in tasks: t = Mythread(que) #把同一个队列传入2个线程 t.start() que.join() print('---success---') 如果把self.queue.task_done() 注释去掉,就会顺利执行完主程序。这就是“Queue.task_done()函数向任务已经完成的队列发送一个信号”这句话的意义,能够让join()函数能判断出队列还剩多少,是否清空了。而事实上我们看下queue的源码可以看出确实是执行一次未完成队列减一: def task_done(self): '''Indicate that a formerly enqueued task is complete. Used by Queue consumer threads. For each get() used to fetch a task, a subsequent call to task_done() tells the queue that the processing on the task is complete. If a join() is currently blocking, it will resume when all items have been processed (meaning that a task_done() call was received for every item that had been put() into the queue). Raises a ValueError if called more times than there were items placed in the queue. ''' with self.all_tasks_done: unfinished = self.unfinished_tasks - 1 if unfinished <= 0: if unfinished < 0: raise ValueError('task_done() called too many times') self.all_tasks_done.notify_all() self.unfinished_tasks = unfinished 快速生产-快速消费上面的演示代码是快速生产-慢速消费的场景,我们可以直接用task_done()与join()配合,来让empty()判断出队列是否已经结束。 当然,queue我们可以正确判断是否已经清空,但是线程里的get队列是不知道,如果没有东西告诉它,队列空了,因此get还会继续阻塞,那么我们就需要在get程序中加一个判断,如果empty()成立,break退出循环,否则get()还是会一直阻塞。慢速生产-快速消费但是如果生产者速度与消费者速度相当,或者生产速度小于消费速度,则靠task_done()来实现队列减一则不靠谱,队列会时常处于供不应求的状态,常为empty,所以用empty来判断则不靠谱。 那么这种情况会导致 join可以判断出队列结束了,但是线程里不能依靠empty()来判断线程是否可以结束。 我们可以在消费队列的每个线程最后塞入一个特定的“标记”,在消费的时候判断,如果get到了这么一个“标记”,则可以判定队列结束了,因为生产队列都结束了,也不会再新增了。 代码如下:'''threading test''' import threading import queue from time import sleep #之所以为什么要用线程,因为线程可以start后继续执行后面的主线程,可以put数据,如果不是线程直接在get阻塞。 class Mythread(threading.Thread): def __init__(self,que): threading.Thread.__init__(self) self.queue = que def run(self): while True: item = self.queue.get() self.queue.task_done() #这里要放到判断前,否则取最后最后一个的时候已经为空,直接break,task_done执行不了,join()判断队列一直没结束 if item == None: break print(item,'!') return que = queue.Queue() tasks = [Mythread(que) for x in range(1)] #快速生产 for x in tasks: t = Mythread(que) #把同一个队列传入2个线程 t.start() for x in range(10): sleep(1) que.put(x) for x in tasks: que.put(None) que.join() print('---success---') 注意点:put队列完成的时候千万不能用task_done(),否则会报错:task_done() called too many times 因为该方法仅仅表示get成功后,执行的一个标记。
2017年08月07日
1,347 阅读
0 评论
0 点赞
2017-08-07
Python yield 与 yield from
Python yield 与 yield from yield使用 1)函数中使用yield,可以使函数变成生成器。一个函数如果是生成一个数组,就必须把数据存储在内存中,如果使用生成器,则在调用的时候才生成数据,可以节省内存。 2)生成器方法调用时,不会立即执行。需要调用next()或者使用for循环来执行。使用for循环不需要自己捕获StopIteration异常。使用next()方法,当生产器方法执行结束会抛出StopIteration异常(只要不是使用yield返回数据,都会抛出StopIteration异常)。 示例:def fib(max): n,a,b = 0,0,1 while n < max: yield b a, b = b, a+b n = n + 1 return 'done' n = fib(10) for n1 in n: print(n1) 123456789101112 3)yield不仅可以返回值,也可以接收值。下面面示例为生产消费模式。生产者生产一条记录,消费者消费一条记录。 4)调用生成器send方法传递数据时,必须先调用next(c)或者c.send(None)方法,执行到yield语句,等待接收数据。否则会报错。 以下代码为廖雪峰网站的示例https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001432090171191d05dae6e129940518d1d6cf6eeaaa969000def consumer(): r = '' while True: n = yield r if not n: return print('[CONSUMER] Consuming %s...' % n) r = '200 OK' def produce(c): c.send(None) # 和next方法一样 获取下一个值,必须先使用None参数调用一次, 执行到yield n = 0 while n < 5: n = n + 1 print('[PRODUCER] Producing %s...' % n) r = c.send(n) # 先发送值给yield语句,再执行到yield语句时返回 print('[PRODUCER] Consumer return:%s' % r) c.close() c = consumer() produce(c)12345678910111213141516171819202122 2、yield from的使用 1)为了让生成器(带yield函数),能简易的在其他函数中直接调用,就产生了yield from。 2)以下代码,htest为生成器,itest通过yield from 直接调用htest。这样itest也变成了一个生成器。创建itest实例不断的去获取数据,当生成器执行结束时,会抛出StopIteration异常。那这个异常是htest抛出的,还是itest抛出的。通过捕获异常,会发现其实是itest抛出异常,htest并不会抛出StopIteration异常。 3)yield from 也可以返回值,通过变量接收。变量接收的值,即htest使用return返回的值。示例代码中,当i==3时,会直接使用return返回,这时val的值就是100;因为htest函数中不是使用yield返回值,所以itest会继续执行print(val)语句。itest代码执行完,然而并没有使用yield返回数据(htest中没有,itest中也没有),所以马上会抛出StopIteration异常)(如果在itest函数最后使用yield返回一个数据,就不会抛出异常)。def htest(): i = 1 while i < 4: n = yield i if i == 3: return 100 i += 1 def itest(): val = yield from htest() print(val) t = itest() t.send(None) j = 0 while j < 3: j += 1 try: t.send(j) except StopIteration as e: print('异常了')12345678910111213141516171819202122
2017年08月07日
1,247 阅读
0 评论
0 点赞
2017-08-07
Python的多任务 03 协程
什么是协程协程是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。 为啥说它是一个执行单元,因为它自带CPU上下文。这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程。 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的。通俗的讲:在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定协程和线程的差异在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住协程其实就是一个可以暂停执行的函数,并且可以恢复继续执行。那么yield已经可以暂停执行了,如果在暂停后有办法把一些 value 发回到暂停执行的函数中,那么 Python 就有了『协程』。从技术的角度来说,“协程就是你可以暂停执行的函数”与生成器类似,但yield 出现在表达式的右边,且产出一个表达式值作为返回值,如果yield后没有表达式,则返回None。协程可以从调用方接收数据,caller.send(params)。1. 例子>>> def simple(a): print "start:a = ", (a) b = yield a print "received b= ",(b) c = yield a + b print "received :d ", (c) >>> m1 = simple(2) >>> m1 <generator object simple at 0x0000000003D830D8> >>> next(m1) # 激活协程, 并产出a,然后暂停 start:a = 2 2 >>> m1.send(3) received b= 3 5 >>> m1.send(1000) # b 接收数据,并产出 a+b 的结果,然后暂停 received :d 1000 Traceback (most recent call last): File "<pyshell#11>", line 1, in <module> m1.send(1000) StopIteration 把yield作为控制流程的方式来理解。上图中 第一阶段 yield a结束,第二阶段是给b 赋值开始再看一个例子:def averager(): total = 0 count = 0 avg = None while True: term = yield avg total += term count +=1 avg = total/count ag = averager() next(ag) # 运行到 yield avg 就停止,并返回avg值 None ag.send(100) # item 接收到100,并运行,直到下一个yield 并停止 2. 与协程相关的方法我们知道了怎么创建协程,但是当执行到最后,如果不处理就会和生成器一样抛出一个stopInteration异常来终止该协程,从2.5版本开始添加了两个方法throw 和close来终止协程如果generator.thow(exception)的异常被捕获,则继续下一个yield,否则终止3. 协程返回值在定义体中 return valuefrom collections import namedTuple Result = namedTuple("Result","count average") def average(): total = 0 count = 0 avg = None while True: term = yield if term is None: break # 终止符 total +=term count +=1 avg = total/count return Result(count,avg) 当send(None) 就会break, 返回。其中Result 会作为异常的value属性返回try: m2 .send(None) exception StopInteration as exc: print exc.value 应用# BEGIN YIELD_FROM_AVERAGER from collections import namedtuple Result = namedtuple('Result', 'count average') # the subgenerator def averager(): # <1> total = 0.0 count = 0 average = None while True: term = yield # <2> if term is None: # <3> break total += term count += 1 average = total/count return Result(count, average) # <4> # the delegating generator def grouper(results, key): # <5> while True: # <6> results[key] = yield from averager() #生成一个使用协程的生成器,在此处暂停,等 将一个Result 赋值给对应的key # the client code, a.k.a. the caller def main(data): # <8> results = {} for key, values in data.items(): group = grouper(results, key) # group是grouper函数生成的生成器对象 next(group) # 预激活协程 for value in values: # 把各个 value 传给 grouper。传入的值最终到达 averager 函数中 term = yield 那一行; grouper 永远不知道传入的值是什么 group.send(value) # 内层循环结束后, group 实例依旧在 yield from 表达式处暂停,因此, grouper函数定义体中为 results[key] 赋值的语句还没有执行 #把 None 传入 grouper,导致当前的 averager 实例终止,也让 grouper 继续运行,再创建一个 averager 实例,处理下一组值 group.send(None) # print(results) # uncomment to debug report(results) # output report def report(results): for key, result in sorted(results.items()): group, unit = key.split(';') print('{:2} {:5} averaging {:.2f}{}'.format( result.count, group, result.average, unit)) data = { 'girls;kg': [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5], 'girls;m': [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43], 'boys;kg': [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3], 'boys;m': [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46], } if __name__ == '__main__': main(data) 上述例子,如果子生成器不停止,委派生成器永远在yield from 处停止。yield from iterable本质上等于for item in iterable: yield item的缩写yield from的意义把迭代器当作生成器使用,相当于把子生成器的定义体内联在 yield from 表达式 中。此外,子生成器可以执行 return 语句,返回一个值,而返回的值会成为 yield from 表达式的值 子生成器产出的值都直接传给委派生成器的调用方(即客户端代码将上述例子的averager 直接给了main中的group)。 使用 send() 方法发给委派生成器的值都直接传给子生成器。如果发送的值是 None,那么会调用子生成器的 __next__()方法。如果发送的值不是None,那么会 调用子生成器的 send()方法。如果调用的方法抛出 StopIteration 异常,那么委 派生成器恢复运行。任何其他异常都会向上冒泡,传给委派生成器。 生成器退出时,生成器(或子生成器)中的return expr表达式会触发 StopIteration(expr)异常抛出。 yield from 表达式的值是子生成器终止时传给 StopIteration 异常的第一个参 数。 yield from 结构的另外两个特性与异常和终止有关。 传入委派生成器的异常,除了GeneratorExit之外都传给子生成器的throw() 方 法。如果调用 throw()方法时抛出StopIteration 异常,委派生成器恢复运 行。 StopIteration 之外的异常会向上冒泡,传给委派生成器。 如果把GeneratorExit 异常传入委派生成器,或者在委派生成器上调用close()方 法,那么在子生成器上调用close()方法,如果它有的话。如果调用close() 方法 导致异常抛出,那么异常会向上冒泡,传给委派生成器;否则,委派生成器抛出 GeneratorExit异常。
2017年08月07日
3,724 阅读
0 评论
0 点赞
2017-08-07
Python的多任务 02
线程概念程序中一个可以执行的分支程序默认只有一个执行分支, 就是主线程想要完成多任务,可以再开辟一个线程,好比再有一个可以执行代码的分支。可以就可以完成多个任务同时在执行。线程是cpu调度的基本单位 在python里,使用threading模块来进行程序的多线程操作t = threading.Thread(target=saySorry) t.start() #启动线程,即让线程开始执行 使用threading.Thread创建线程 length = len(threading.enumerate()) #查看进程的数量 # threading.enumerate()是当前程序所有进行中的线程的列表,通过统计其长度,就可以得出当前有多少个线程在进行 threading.current_thread() #查看当前任务执行的线程 # 放在代码中的任意地方即可知道当前代码执行的是哪个线程,如果是放在函数里,则显示是执行子线程 将子线程设置成为守护主线程,主线程退出了,那么子线程直接销毁不执行代码了, 以后线程的销毁要依赖主线程调用setDaemon(True) 子线程名.setDaemon(True) join() 等待子线运行结束后再开启下一个子线程t1 = threading.Thread(target=sing, args=(5,), kwargs={"name": "insmoin", "age": 18}) 创建线程,此处sing后面不要带有小括号,只要有函数名即可1.target是线程执行函数的名字,函数的名字后面不要带有小括号,注意与函数调用的方法区别开来2.args:执行函数所需要的参数,这个参数要以元组的形式去传,如果只有一个元素,后面不要忘了逗号3.kwargs:执行函数所需要的参数, 这个参数要以字典方式去传注意: 调用完start方法以后线程才会放入到线程活动列表中线程注意点过使用threading模块能完成多任务的程序开发,为了让每个线程的封装性更完美,所以使用threading模块时,往往会定义一个新的子类class,只要继承threading.Thread就可以了,然后重写run方法当定义新的子类时,创建好实例对象后,不是通过调用run方法开启线程,而是通过对象.start()开启线程python的threading.Thread类有一个run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。而创建自己的线程实例后,通过Thread类的start方法,可以启动该线程,交给python虚拟机进行调度,当该线程获得执行的机会时,就会调用run方法执行线程。 主线程会等待所有的子线程结束后才结束 线程之间执行顺序是无序, 主线程会等待所有的子线程任务执行完成以后程序再退出 多线程共享全局变量的优缺点 优点: 在一个进程内的所有线程共享全局变量,很方便在多个线程间共享数据 缺点: 线程是对全局变量随意遂改可能造成多线程之间对全局变量的混乱(即线程非安全)如果多个线程同时对同一个全局变量操作,会出现资源竞争问题,从而数据结果会不正确 互斥锁当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性死锁在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。进程概念一个程序运行起来后, 代码+用到的资源, 就称为进程, 是操作系统分配资源的基本单位 Python进程的使用导入模块import multiprocessing获取当前进程编号导入模块import osOs.getpid() 获取当前的进程号在子进程里面获取当前子进程的父进程的编号用的是os.getppid(),注意与os.getpid的区别multiprocessing.current_process().pid也可以获取当前的进程编号os.kill() 根据指定的进程的编号杀死指定的进程查看当前任务由哪个进程执行multiprocessing.current_process() 创建进程实例名 = multiprocessing.Process 进程之间的通讯Queue导入模块import multiprocessing初始化一个Queue对象q=multiprocessing.Queue(5) # 括号内的数字代表能够接收的消息数量 # 消息可以是任意的数据类型,字符串,数字,列表,元组,字典,集合等 # 若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到内存的尽头) Queue.qsize() # 返回当前队列包含的消息数量 Queue.empty() # 如果队列为空,返回True,反之False Queue.full() # 如果队列满了,返回True,反之False Queue.get([block[, timeout]]) # 获取队列中的一条消息,然后将其从列队中移除,block默认值为True # 如果block使用默认值,且没有设置timeout(单位秒),消息列队如果为空,此时程序将被阻塞(停在读取状态),直到从消息列队读到消息为止,如果设置了timeout,则会等待timeout秒,若还没读取到任何消息,则抛出"Queue.Empty"异常; # 如果block值为False,消息列队如果为空,则会立刻抛出"Queue.Empty"异常 Queue.get_nowait() # 相当于 Queue.get(False) Queue.put_nowait(item) # 相当 Queue.put(item, False) 进程池进程池执行任务的本质: 从进程池中找一个空闲的进程去执行指定的任务进程池使用同步方式和异步方式执行的区别进程池有两种执行方式,即同步执行和异步执行,用同步执行的话,主进程会等待进程池里面的任务执行完毕后才开始执行主进程的任务,如果是异步执行的话,则,主进程不会理会进程池里面执行任务的情况,而是直接执行主进程里面的任务简单来说,同步执行的话,主进程会等待进程池里面的任务执行完毕,而异步执行的话,主进程是不会等待进程池里面执行任务完毕的 使用import multiprocessing # 导入模块 multiprocessing.Pool() #创建进程池, 括号内可以放上数字表示该进程池拥有的进程数量 close() # 关闭Pool,使其不再接受新的任务 terminate() # 不管任务是否完成,立即终止 join() # 主进程阻塞,等待子进程的退出, 必须在close或terminate之后使用 apply(copy_work) # 进程池使用同步方式执行这个任务,进程池用的进程会按照一定顺序等待其它进程执行完成以后再依次执行 迭代器概念迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。我们把可以通过for...in...这类语句迭代读取一条数据供我们使用的对象称之为可迭代对象(Iterable) 可迭代对象和不可迭代对象我们把可以通过for...in...这类语句迭代读取一条数据供我们使用的对象称之为可迭代对象(Iterable) 可迭代对象: 列表,元组,字符串,字典,集合,range等 不可迭代对象: 整型,自定义的类等 判断可迭代对象from collections import Iterable isinstance([], Iterable) # 结果为True则是可迭代对象 # isinstance要传入两个参数,第一个是要判断的对象,第二个是Iterable,首字母是大写的i 判断迭代器from collections import Iterator isinstance([], Iterator) isinstance(iter([]), Iterator) # 要用iter()函数获取可迭代对象的迭代器,可迭代对象本身不是迭代器来的 可迭代对象的本质就是可以向我们提供一个 中间人即迭代器帮助我们对其进行迭代遍历使用生成器概念只要在def中有yield关键字的 就称为 生成器使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)yield有两点作用 保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起 将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用 可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数) 唤醒生成器# 使用next()函数可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数) f = gen() next(f) # 使用send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。 f = gen() f.send('haha') # 使用__next__()方法 f = gen() f.__next__() 可迭代对象,迭代器,生成器可迭代对象:一个具备了iter方法的对象,就是一个可迭代对象迭代器:任何具有__next__()方法的对象都是迭代器, 对迭代器调用next()方法可以获取下一个值迭代器本质上是一个产生值的工厂,每次向迭代器请求下一个值,迭代器都会进行计算出相应的值并返回。生成器生成器是一个返回迭代器的函数,其实就是定义一个迭代算法,可以理解为一个特殊的迭代器。调用这个函数就得到一个迭代器,生成器中的yield相当于一个断点,执行到此返回一个值后暂停,从而实现next取值。生成器是Python中一种非常强大的特性,它让我们能够编写更加简洁的代码,同时也更加节省内存,使用CPU也更加高效总结:1.迭代器一定是迭代对象,迭代对象不一定是迭代器2.生成器一定是迭代器,迭代器不一定是生成器
2017年08月07日
834 阅读
0 评论
0 点赞
2017-08-07
Python的多任务 01
多任务多任务就是操作系统可以同时运行多个任务并行指的是任务数小于等于cpu核数,即任务真的是一起执行的并发指的是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务“一起”执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)线程和进程进程:是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位。进程和线程的区别 进程通俗理解就是一个运行起来的程序或者软件就是一个进程,每次启动进程都会向操作系统索要运行资源,进程是操作系统资源分配的基本单位。 线程就是程序中执行的一个分支(实例), 每个分支可以执行不同的任务,可以完成多任务操作。 线程是cpu调度的基本单位。 线程是依附在进程里面的,没有进程就没有线程。 进程之间不共享全局变量,线程之间共享全局变量 每次启动进程都会向操作系统索要资源,线程之间共享进程的运行资源 多进程开发模式比多线程开发模式健壮性要强,因为多进程开发某个进程挂掉不会影响其它进程的运行,但是多线程开发,如果进程挂了,那么进程里面所有的线程都会销毁。
2017年08月07日
2,056 阅读
0 评论
0 点赞
2017-08-07
Python 基础概念简单总结
面向对象设计(OOP)面向过程与面向对象面向过程:根据业务逻辑从上到下写代码面向对象:面向对象是向现实世界模型的延伸, 任何物体都可以归为一类事物, 这类事物可以有很多方法和属性, 每一个个体都是一类事物的实例.面向对象有三大特性, 封装, 继承和多态封装就是将一类事物属性和行为抽象成一个类, 使其属性私有化, 行为公开化, 提高数据隐秘性, 使代码模块话, 这样使得代码复用性很高继承就是进一步将一类事物公有的属性和行为抽象成一份父类, 子类可以继承父类的方法和属性, 也有自己特有的方法和属性, 这样做扩展了已存在的代码, 进一步提高了代码的复用性.多态就是继承父类的方法后可以修改父类的方法, 就是鸭子类型, 看起来像鸭子, 走起来像鸭子,那它就是鸭子.Python的类python2与python3中新式类和经典类的区别Python3 里如果一个类没有写父类,则默认继承object。 继承的这个object就是新式类Python2 里如果一个类写了继承 object 则是 新式类,如果没写父类则是 经典类重写父类当子类重写了父类的方法后,有三种方法可以在子类调用父类的属性和方法 父类名.方法名(self,xxx,xxx,xxx,...) Animal.init(self) super().方法名(xx,xx,xx),注意,参数里不需要传 self。只在 python3 可用 super().init() super(子类名, 子类对象).方法名(xx,xx,xxx) super(Cat, self).init() 静态方法和类方法静态方法:不需要定义实例就可以使用这个方法.定义方法事需要在函数前面加上@staticmethod标记符类方法:定义类方法需要在函数前加上@classmethodcls代表类对象本身类方法可以通过类或其他实例来调用该方法__new__方法在实例化对象的时候, 在调用初始化函数之前, 先调用了__new__()方法__new__方法必须要返回一个实例化的对象__new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供继承自object的新式类才有__new__如果将类比喻为工厂,那么init()方法则是该工厂的生产工人,init()方法接受的初始化参 数则是生产所需原料,init()方法会按照方法中的语句负责将原料加工成实例以供工厂出货。而 new()则是生产部经理,new()方法可以决定是否将原料提供给该生产部工人,同时它还决定着出 货产品是否为该生产部的产品,因为这名经理可以借该工厂的名义向客户出售完全不是该工厂的产品。单例模式单例模式是一种常见的设计模式, 主要目的是确保某个类只有一个实例存在实现单例模式的几种方法:class Person(object): instance = None # 定义一个类属性 instance,用来记录第一次创建的对象 def __new__(cls, *args, **kwargs): if cls.instance == None: # 还没有创建第一个对象 cls.instance = object.__new__(cls) # 使用类属性来引用创建的第一个对象 return cls.instance def __init__(self, name): self.name = name 单例模式单例模式是一种常见的设计模式, 主要目的是确保某个类只有一个实例存在实现单例模式的几种方法:class Person(object): instance = None # 定义一个类属性 instance,用来记录第一次创建的对象 def __new__(cls, *args, **kwargs): if cls.instance == None: # 还没有创建第一个对象 cls.instance = object.__new__(cls) # 使用类属性来引用创建的第一个对象 return cls.instance def __init__(self, name): self.name = name 异常处理异常当Python检测到一个错误时,解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的"异常"捕获异常try: except: try语句中放入可能产生错误的代码 except语句中放入处理错误的代码 捕捉指定异常语法:except 异常名一次性捕捉多个异常语法: except (异常1, 异常2,..)捕捉任意异常except Exception:Python 里大部分的异常都是 Exception 的子类,所以都可以使用 Exception 来捕捉通过别名获取异常信息单个except 异常名 as 别名多个except (变量1, 变量2,..) as exp异常else,finally如果没有捕获到异常,那么就执行else中的事情在程序中,如果一个段代码必须要执行,即无论异常是否产生都要执行,那么此时就需要使用finally。异常的传递如果try嵌套,那么如果里面的try没有捕获到这个异常,那么外面的try会接收到这个异常,然后进行处理,如果外边的try依然没有捕获到,那么再进行传递简单来说就是由内向外自定义异常数据类型可变数据类型列表和字典修改时,内存地址不发送改变不可变数据类型数字,字符串,元组修改时,内存地址发生改变匿名函数用lambda关键词能创建小型匿名函数。这种函数得名于省略了用def声明函数的标准步骤语法格式: lambda [arg1 [,arg2,.....argn]]:expression
2017年08月07日
1,060 阅读
0 评论
0 点赞
1
2