Ⅰ 深入浅出python怎么样
Head First 系列的书籍一直饱受赞誉,这本也不例外。Head First Python主要讲述了Python 3的基础语法知识以及如何使用Python快速地进行Web、手机上的开发。 下面是该书每章结束部分的知识摘要: 第一章 初始Python:人人都爱列表(Lists) 1. 从命令行或者IDLE里都可以运行Python 3; 2. 标识符是指代数据对象的名称,它本身并没有“类型”,但是它所指代的数据对象拥有类型; 3. 内置函数print()可以在屏幕上显示消息; 4. Python中的列表list是用中括号包住的以逗号分隔的数据集合; 5. list和数组非常相似; 6. list既可以使用内置函数,也可以使用针对列表本身的函数; 7. list的大小按需自动伸缩。数据使用的所有内存都由Python管理; 8. len()内置函数用来计算数据对象的长度或是某个集合(如list)内条目的数量; 9. for循环可以帮助遍历list,它用起来通常比等价的while循环更方便; 10. if...else...语句帮助在代码中做出不同的选择; 11. isinstance()内置函数可用来检测标识符指代的数据对象是否为指定类型; 12. 使用def定义自定义函数。 第二章 分享代码:函数模块 1. 模块是包含Python代码的文本文件; 2. 分发工具(distribution utilities)帮助您将模块变为可共享的包; 3. setup.py程序提供了关于模块的元数据,他可以用于生成,安装和上传分发包; 4. 使用import语句将模块导入到其他程序中; 5. Python中每个模块都提供了自己的命名空间。它在使用mole.function()形式调用时用以限定模块内的函数。 6. 使用形如from mole import function的import语句可以将模块内特定函数导入当前命名空间; 7. 使用#可以将一行代码变为注释或者为程序添加一条精短且在一行内的注释; 8. 内置函数拥有自己的命名空间,叫做__builtins__,它会自动包含进每个Python程序中。 9. range()内置函数可以和for用在一起进行固定次数的遍历; 10. 在print()内置函数结尾包含end=''可以关掉输出时自动添加的换行符; 11. 如果为函数参数提供默认值,那么它们就为成为可选参数。 第三章 文件和异常:处理错误 1. 使用open()内置函数打开磁盘文件并创建一个迭代器来每次从文件中读取一行数据; 2. readline()方法从一个打开的文件中读取一行数据; 3. seek()方法可以将文件重新定位到开头; 4. close()方法关闭上一次打开的文件; 5. split()方法将一个字符串分为许多份组成的列表; 6. Python中不可改变的常量list叫做tuple。一旦列表数据复制给一个tuple之后,tuple中的数据将不能再被改变。Tuple是不能变的(immutable); 7. 当数据与期望的格式有出入时,会产生ValueError; 8. 当数据没法被正确访问时(例如数据文件可能已经移动过或者重命名过),会产生IOError; 9. help()内置函数提供在IDLE shell中访问Python文档; 10. find()方法可以在一个字符串中查找特定子串; 11. not关键字用来否定一个条件; 12. try/except语句提供了异常处理机制,可以保护那些可能导致运行时错误的代码段; 13. pass语句是Python中的空语句,它什么都不做。 第四章 持久化:将数据存成文件 1. strip()方法移除字符串首尾空白字符; 2. print()内置函数中的file参数可以控制data是读入或是写出; 3. 不管try/except语句中是否有异常发生,finally的代码段总是会被执行; 4. 异常对象会传入到except代码段,并且可以使用as关键字将其赋值给一个标识符; 5. str()内置函数可以用来访问任何数据对象的字符串表示,前提是该数据对象支持该转换; 6. locals()内置函数返回当前作用范围内的变量集合; 7. in操作符可用于测试成员包含关系; 8. "+"操作符应用于两个字符串时会得到它们的串联结果,而应用于数字时会得到它们的相加和; 9. with语句即使在异常发生的情况下,也会自动去关闭所有打开的文件。with语句同样可以使用as关键字; 10. sys.stdout是Python中的标准输出,它位于标准库中的sys模块; 11. 标准库pickle模块可以让轻松高效地保存Python数据对象到磁盘和从磁盘恢复Python数据对象; 12. pickle.mp()函数将数据存盘; 13. pickle.load()函数从磁盘恢复数据。 第五章 理解数据:让数据动起来 1. sort()方法原地排序列表; 2. sorted()内置函数通过复制排序的方式可以对大多数数据结构进行排序; 3. 传入sort()或sorted的参数reverse=True可以将数据进行降序排序; 4. 形如下面的代码段: new_l = [] for t in old_l: new_l.append(len(t)) 可以重写为列表表达式形式:[len(t) for t in old_l] 5. 使用切片从list中获取多个数据条目,如:my_lis[3:6]会从索引3的位置访问到索引6的位置,不包含6。 6. 使用set()工厂方法创建一个集合 第六章 自定义数据对象:围绕数据编码 1. 使用dict()工厂函数或使用{}来创建一个空的dictionary; 2. 访问一个叫做person的dictionary中Name键所对应的值,可是使用熟悉的中括号记法:person['Name']; 3. 同list和set类似,Python的dictionary数据结构也会随着新元素的加入动态的增长; 4. 填充dictionary的方法有:new_d = {}或new_d = dict(),然后使用d['Name'] = 'Eric Idle';或者直接用一句话new_d = {'Name': 'Eric Idle'}; 5. class关键字用来定义类; 6. 类中方法的定义与函数非常相像,都使用def关键字; 7. 类中属性就如同对象实例内部的变量; 8. __init__()方法可以定义在类中用作实例化对象实例; 9. 类中定义的每个方法都必须提供self作为第一个参数; 10. 类中的每个属性都必须使用self为前缀,以使得数据能与实例关联在一起; 11. 既可以从头开始创建类也可以从Python内置或自定义类中继承; 12. 类可以被放入Python模块并上传到PyPI。 第7章 Web开发:信息汇总 1. MVC模式(Model-View-Controller)用一种可维护的方式帮助设计和构建一个Web应用; 2. model存储Web应用中的数据; 3. view显示Web应用的用户界面; 4. controller使用编程逻辑将所有部分连接在一起; 5. 标准库string模块中有一个类叫做Template,它支持简单的字符串替换; 6. 标准库http.server模块可以用来在Python中创建一个简单的Web服务器; 7. 标准库cgi模块提供编写CGI脚本的支持; 8. 标准库glob模块处理文件列表非常好用; 9. 在Linux和Mac OS X上为可执行文件执行chmod+x命令; 10. 标准库cgitb模块在激活时可以在浏览器中看到CGI的编码错误; 11. 使用cgitb.enable()可以在CGI代码中打开CGI跟踪; 12. 使用cgi.FieldStorage()可以访问发送到Web服务器请求部分的数据。 第8章 移动应用开发:小型设备 1. json库模块可以将Python内置类型转为JSON数据交换格式; 2. 使用json.mps()可以创建Python类型的字符串版本; 3. 使用json.loads()从JSON字符串中创建Python类型; 4. 使用JSON发布数据需要制定Content-Type为application/json; 5. Python 2中的urllib和urllib2可以用与发送编码后的数据给Web服务器(使用urlencode和urlopen函数); 6. sys模块提供了sys.stdin, sys.stdout和sys.stderr输入流。 第9章 管理数据:处理输入 1. 标准库cgi模块中的fieldStorage()方法可以访问CGI脚本中发送给Web服务器的数据; 2. 标准库os中包含的environ字典,提供了对环境变量设置的轻松访问; 3. SQLLite数据库系统在Python中作为sqlite3标准库存在; 4. connect()方法建立与数据库文件的连接; 5. cursor()方法通过一个已有连接与数据库进行通信; 6. execute()方法通过一个已有游标向数据库发送SQL查询; 7. commit()方法对数据库做出永久性的改变; 8. rollback()方法取消任何针对数据的待定改动; 9. close()方法会关闭数据库的已有连接; 10. Python代码中的"?"占位符可以参数化SQL语句。 第10章 扩展Web应用:变得真实 1. 每个App Engine Web应用都必须有一个叫做app.yaml的配置文件; 2. 使用GAE启动器启动、停止、监控、测试、上传以及部署Web应用; 3. App Engine的模板技术基于Django项目; 4. App Engine也可以使用Django表单验证框架; 5. 使用self.response对象构造一个GAE Web应答; 6. 使用self.request对象在GAE Web应用中访问表单数据; 7. 当应答GET请求时,可以子啊get()方法中实现需要的功能; 8. 当应答POST请求时,在post()方法中实现需要的功能; 9. 使用put()方法将数据存储到App Engine datastore中。 第11章 处理复杂数据 1. input()内置函数提示并接受来自用户的输入; 2. 如果发现在使用的是Python2,可以使用原生的_input()函数来取代input()函数; 3. 使用Python内置的列表、集合及字典构建复杂数据结构; 4. 标准库中的time模块,有大量函数可以用来转换不同的时间格式。 书中没有谈到的10件重要的事 #1. 使用专业的IDE 书中推荐的是WingWare Python IDE。我一直在用的是Eclipse + PyDev #2. 积极面对作用域 使用global关键字可以强制将全局变量放入当前作用域 #3. 测试 书中提到了两个用于测试的框架,一个是Python中的unittest模块;另一个也是标准库中的doctest #4. 高级语言特性 匿名函数、生成器、自定义异常、函数修饰符、元数据类等 #5. 正则表达式 #6. 更多的Web框架 Django、Zope、TurboGears、Web2py、Pylons等 #7. 对象关系映射(ORM)以及NoSQL ORM工具:SQL Alchemy NoSQL数据库:CouchDB和MongoDB #8. 用户界面编程 Python内置的跨平台的GUI构建工具集——tkinter(Tk Interface)。 其他的GUI编程技术有:PyGTK,PyKDE,WxPython和PyQT等。 #9. 避免使用多线程 Python中的全局解释锁限定Python只能运行单个解释进程,即使多核情况下也不行 #10. 书籍推荐 Dive into Python 3 Python Essential Reference Programming in Python Learning Python ...
Ⅱ Python怎么深入模块进行学习
6.1. 深入模块
除了包含函数定义外,模块也可以包含可执行语句。这些语句一般用来初始化模块。他们仅在 第一次 被导入的地方执行一次。[1]
每个模块都有自己私有的符号表,被模块内所有的函数定义作为全局符号表使用。因此,模块的作者可以在模块内部使用全局变量,而无需担心它与某个用户的全局变量意外冲突。从另一个方面讲,如果你确切的知道自己在做什么,你可以使用引用模块函数的表示法访问模块的全局变量,modname.itemname。
模块可以导入其他的模块。一个(好的)习惯是将所有的 import 语句放在模块的开始(或者是脚本),这并非强制。被导入的模块名会放入当前模块的全局符号表中。
import 语句的一个变体直接从被导入的模块中导入命名到本模块的语义表中。例如:
>>> from fibo import fib, fib2
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
这样不会从局域语义表中导入模块名(如上所示, fibo 没有定义)。
甚至有种方式可以导入模块中的所有定义:
>>> from fibo import *
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
这样可以导入所有除了以下划线( _ )开头的命名。
需要注意的是在实践中往往不鼓励从一个模块或包中使用 * 导入所有,因为这样会让代码变得很难读。不过,在交互式会话中这样用很方便省力。
Ⅲ 哪些专业会深入学习到python
就我所知的专业需要学习python专业课的有(基本上数据结构学得越深,python就学得越深):软件工程、通信工程、物联网、机器人、信息管理、信息技术、计算机科学……
Ⅳ Python类的多重继承问题深入分析
Python类的多重继承问题深入分析
首先得说明的是,Python的类分为经典类 和 新式类
经典类是python2.2之前的东西,但是在2.7还在兼容,但是在3之后的版本就只承认新式类了
新式类在python2.2之后的版本中都可以使用
经典类和新式类的区别在于:
经典类是默认没有派生自某个基类的,而新式类是默认派生自object这个基类的:
代码如下:
# old style
class A():pass
# new style
class A(obejct):pass
2.经典类在类多重继承的时候是采用从左到右深度优先原则匹配方法的..而新式类是采用C3算法(不同于广度优先)进行匹配的
3.经典类是没有__MRO__和instance.mro()调用的,而新式类是有的.
为什么不用经典类,要更换到新式类
因为在经典类中的多重继承会有些问题...可能导致在继承树中的方法查询绕过后面的父类:
代码如下:
class A():
def foo1(self):
print "A"
class B(A):
def foo2(self):
pass
class C(A):
def foo1(self):
print "C"
class D(B, C):
pass
d = D()
d.foo1()
按照经典类的查找顺序从左到右深度优先的规则,在访问d.foo1()的时候,D这个类是没有的..那么往上查找,先找到B,里面没有,深度优先,访问A,找到了foo1(),所以这时候调用的是A的foo1(),从而导致C重写的foo1()被绕过.
所以python引入了新式类的概念,每个基类都继承自object并且,他的匹配规则也从深度优先换到了C3
C3算法
C3算法是怎么做匹配的呢..在问答版块上面讨论之后,归结如下:
C3算法的一个核心是merge.
在merge列表中,如果第一个序列mro的第一个类是出现在其它序列,并且也是第一个,或者不出现其它序列,那么这个类就会从这些序列中删除,并合到访问顺序列表中
比如:(引用问题中zhuangzebo的回答@zhuangzebo)
代码如下:
class A(O):pass
class B(O):pass
class C(O):pass
class D(A,B):pass
class E(C,D):pass
首先需要知道 O(object)的mro(method resolution order)列表是[O,]
那么接下来是:
代码如下:
mro(A) = [A, O]
mro(B) = [B, O]
mro(C) = [C, O]
mro(D) = [D] + merge(mro(A), mro(B), [A, B])
= [D] + merge([A, O], [B, O], [A, B])
= [D, A] + merge([O], [B, O], [B])
= [D, A, B] + merge([O], [O])
= [D, A, B, O]
mro(E) = [E] + merge(mro(C), mro(D), [C, D])
= [E] + merge([C, O], [D, A, B, O], [C, D])
= [E, C] + merge([O], [D, A, B, O], [D])
= [E, C, D] + merge([O], [A, B, O])
= [E, C, D, A, B] + merge([O], [O])
= [E, C, D, A, B, O]
然后还有一种特殊情况:
比如:
merge(DO,CO,C) 先merge的是D
merge(DO,CO,C) 先merge的是C
意思就是.当出现有 一个类出现在两个序列的头(比如C) 这种情况和 这个类只有在一个序列的头(比如D) 这种情况同时出现的时候,按照顺序方式匹配。
新式类生成的访问序列被存储在一个叫MRO的只读列表中..
你可以使用instance.__MRO__或者instance.mro()来访问
最后匹配的时候就按照MRO序列的顺序去匹配了
C3和广度优先的区别:
举个例子就完全明白了:
代码如下:
class A(object):pass
class B(A):pass
class C(B):pass
class D(A):pass
class E(D):pass
class F(C, E):pass
按照广度优先遍历,F的MRO序列应该是[F,C,E,B,D,A]
但是C3是[F,E,D,C,B,A]
意思是你可以当做C3是在一条链路上深度遍历到和另外一条链路的交叉点,然后去深度遍历另外一条链路,最后遍历交叉点
新式类和经典类的super和按类名访问问题
在经典类中,你如果要访问父类的话,是用类名来访问的..
代码如下:
class A():
def __init__(self):
print "A"
class B(A):
def __init__(self):
print "B"
A.__init__(self) #python不会默认调用父类的初始化函数的
这样子看起来没三问题,但是如果类的继承结构比较复杂,会导致代码的可维护性很差..
所以新式类推出了super这个东西...
代码如下:
class A():
def __init__(self):
print "A"
class B(A):
def __init__(self):
print "B"
super(B,self).__init__()
这时候,又有一个问题:当类是多重继承的时候,super访问的是哪一个类呢?
super实际上是通过__MRO__序列来确定访问哪一个类的...实际上就是调用__MRO__中此类后面的一个类的方法.
比如序列为[F,E,D,C,B,A]那么F中的super就是E,E的就是D
super和按照类名访问 混合使用带来的坑
代码如下:
class A(object):
def __init__(self):
print "enter A"
print "leave A"
class B(object):
def __init__(self):
print "enter B"
print "leave B"
class C(A):
def __init__(self):
print "enter C"
super(C, self).__init__()
print "leave C"
class D(A):
def __init__(self):
print "enter D"
super(D, self).__init__()
print "leave D"
class E(B, C):
def __init__(self):
print "enter E"
B.__init__(self)
C.__init__(self)
print "leave E"
class F(E, D):
def __init__(self):
print "enter F"
E.__init__(self)
D.__init__(self)
print "leave F"
这时候打印出来是:
代码如下:
enter F
enter E
enter B
leave B
enter C
enter D
enter A
leave A
leave D
leave C
leave E
enter D
enter A
leave A
leave D
leave F
可以看出来D和A的初始化函数被乱入了两次!
按类名访问就相当于C语言之前的GOTO语句...乱跳,然后再用super按顺序访问..就有问题了
所以建议就是要么一直用super,要么一直用按照类名访问
最佳实现:
避免多重继承
super使用一致
不要混用经典类和新式类
调用父类的时候注意检查类层次
以上便是本人对于python类的继承的认识了,希望对大家能有所帮助
Ⅳ 深入理解python中的排序sort
进行一个简单的升序排列直接调用sorted()函数,函数将会返回一个排序后的列表:
sorted函数不会改变原有的list,而是返回一个新的排好序的list
如果你想使用就地排序,也就是改变原list的内容,那么可以使用list.sort()的方法,这个方法的返回值是None。
另一个区别是,list.sort()方法只是list也就是列表类型的方法,只可以在列表类型上调用。而sorted方法则是可以接受任何可迭代对象。
list.sort()和sorted()函数都有一个key参数,可以用来指定一个函数来确定排序的一个优先级。比如,这个例子就是根据大小写的优先级进行排序:
key参数的值应该是一个函数,这个函数接受一个参数然后返回以一个key,这个key就被用作进行排序。这个方法很高效,因为对于每一个输入的记录只需要调用一次key函数。
一个常用的场景就是当我们需要对一个复杂对象的某些属性进行排序时:
再如:
前面我们看到的利用key-function来自定义排序,同时Python也可以通过operator库来自定义排序,而且通常这种方法更好理解并且效率更高。
operator库提供了 itemgetter(), attrgetter(), and a methodcaller()三个函数
同时还支持多层排序
list.sort()和sorted()都有一个boolean类型的reverse参数,可以用来指定升序和降序排列,默认为false,也就是升序排序,如果需要降序排列,则需将reverse参数指定为true。
排序的稳定性指,有相同key值的多个记录进行排序之后,原始的前后关系保持不变
我们可以看到python中的排序是稳定的。
我们可以利用这个稳定的特性来进行一些复杂的排序步骤,比如,我们将学生的数据先按成绩降序然后年龄升序。当排序是稳定的时候,我们可以先将年龄升序,再将成绩降序会得到相同的结果。
传统的DSU(Decorate-Sort-Undecorate)的排序方法主要有三个步骤:
因为元组是按字典序比较的,比较完grade之后,会继续比较i。
添加index的i值不是必须的,但是添加i值有以下好处:
现在python3提供了key-function,所以DSU方法已经不常用了
python2.x版本中,是利用cmp参数自定义排序。
python3.x已经将这个方法移除了,但是我们还是有必要了解一下cmp参数
cmp参数的使用方法就是指定一个函数,自定义排序的规则,和java等其他语言很类似
也可以反序排列
python3.x中可以用如下方式:
Ⅵ 零基础学习python需要怎么入手
编程零基础的学习Python全栈可以按照以下内容来:
阶段一:Python开发基础
Python全栈开发与人工智能之Python开发基础知识学习内容包括:Python基础语法、数据类型、字符编码、文件操作、函数、装饰器、迭代器、内置方法、常用模块等。
阶段二:Python高级编程和数据库开发
Python全栈开发与人工智能之Python高级编程和数据库开发知识学习内容包括:面向对象开发、Socket网络编程、线程、进程、队列、IO多路模型、Mysql数据库开发等。
阶段三:前端开发
Python全栈开发与人工智能之前端开发知识学习内容包括:Html、CSS、JavaScript开发、Jquery&bootstrap开发、前端框架VUE开发等。
阶段四:WEB框架开发
Python全栈开发与人工智能之WEB框架开发学习内容包括:Django框架基础、Django框架进阶、BBS+Blog实战项目开发、缓存和队列中间件、Flask框架学习、Tornado框架学习、Restful API等。
阶段五:爬虫开发
Python全栈开发与人工智能之爬虫开发学习内容包括:爬虫开发实战。
阶段六:全栈项目实战
Python全栈开发与人工智能之全栈项目实战学习内容包括:企业应用工具学习、CRM客户关系管理系统开发、路飞学城在线教育平台开发等。
阶段七:数据分析
Python全栈开发与人工智能之数据分析学习内容包括:金融量化分析。
阶段八:人工智能
Python全栈开发与人工智能之人工智能学习内容包括:机器学习、图形识别、无人机开发、无人驾驶等。
阶段九:自动化运维&开发
Python全栈开发与人工智能之自动化运维&开发学习内容包括:CMDB资产管理系统开发、IT审计+主机管理系统开发、分布式主机监控系统开发等。
阶段十:高并发语言GO开发
Python全栈开发与人工智能之高并发语言GO开发学习内容包括:GO语言基础、数据类型与文件IO操作、函数和面向对象、并发编程等。
Ⅶ 深入解析Python中的线程同步方法
深入解析Python中的线程同步方法
同步访问共享资源
在使用线程的时候,一个很重要的问题是要避免多个线程对同一变量或其它资源的访问冲突。一旦你稍不留神,重叠访问、在多个线程中修改(共享资源)等这些操作会导致各种各样的问题;更严重的是,这些问题一般只会在比较极端(比如高并发、生产服务器、甚至在性能更好的硬件设备上)的情况下才会出现。
比如有这样一个情况:需要追踪对一事件处理的次数
counter = 0
def process_item(item):
global counter
... do something with item ...
counter += 1
如果你在多个线程中同时调用这个函数,你会发现counter的值不是那么准确。在大多数情况下它是对的,但有时它会比实际的少几个。
出现这种情况的原因是,计数增加操作实际上分三步执行:
解释器获取counter的当前值计算新值将计算的新值回写counter变量
考虑一下这种情况:在当前线程获取到counter值后,另一个线程抢占到了CPU,然后同样也获取到了counter值,并进一步将counter值重新计算并完成回写;之后时间片重新轮到当前线程(这里仅作标识区分,并非实际当前),此时当前线程获取到counter值还是原来的,完成后续两步操作后counter的值实际只加上1。
另一种常见情况是访问不完整或不一致状态。这类情况主要发生在一个线程正在初始化或更新数据时,另一个进程却尝试读取正在更改的数据。
原子操作
实现对共享变量或其它资源的同步访问最简单的方法是依靠解释器的原子操作。原子操作是在一步完成执行的操作,在这一步中其它线程无法获得该共享资源。
通常情况下,这种同步方法只对那些只由单个核心数据类型组成的共享资源有效,譬如,字符串变量、数字、列表或者字典等。下面是几个线程安全的操作:
读或者替换一个实例属性读或者替换一个全局变量从列表中获取一项元素原位修改一个列表(例如:使用append增加一个列表项)从字典中获取一项元素原位修改一个字典(例如:增加一个字典项、调用clear方法)
注意,上面提到过,对一个变量或者属性进行读操作,然后修改它,最终将其回写不是线程安全的。因为另外一个线程会在这个线程读完却没有修改或回写完成之前更改这个共享变量/属性。
锁
锁是Python的threading模块提供的最基本的同步机制。在任一时刻,一个锁对象可能被一个线程获取,或者不被任何线程获取。如果一个线程尝试去获取一个已经被另一个线程获取到的锁对象,那么这个想要获取锁对象的线程只能暂时终止执行直到锁对象被另一个线程释放掉。
锁通常被用来实现对共享资源的同步访问。为每一个共享资源创建一个Lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁,则当前线程需等待其被释放),待资源访问完后,再调用release方法释放锁:
lock = Lock()
lock.acquire() #: will block if lock is already held
... access shared resource
lock.release()
注意,即使在访问共享资源的过程中出错了也应该释放锁,可以用try-finally来达到这一目的:
lock.acquire()
try:
... access shared resource
finally:
lock.release() #: release lock, no matter what
在Python 2.5及以后的版本中,你可以使用with语句。在使用锁的时候,with语句会在进入语句块之前自动的获取到该锁对象,然后在语句块执行完成后自动释放掉锁:
from __future__ import with_statement #: 2.5 only
with lock:
... access shared resource
acquire方法带一个可选的等待标识,它可用于设定当有其它线程占有锁时是否阻塞。如果你将其值设为False,那么acquire方法将不再阻塞,只是如果该锁被占有时它会返回False:
if not lock.acquire(False):
... 锁资源失败
else:
try:
... access shared resource
finally:
lock.release()
你可以使用locked方法来检查一个锁对象是否已被获取,注意不能用该方法来判断调用acquire方法时是否会阻塞,因为在locked方法调用完成到下一条语句(比如acquire)执行之间该锁有可能被其它线程占有。
if not lock.locked():
#: 其它线程可能在下一条语句执行之前占有了该锁
lock.acquire() #: 可能会阻塞
简单锁的缺点
标准的锁对象并不关心当前是哪个线程占有了该锁;如果该锁已经被占有了,那么任何其它尝试获取该锁的线程都会被阻塞,即使是占有锁的这个线程。考虑一下下面这个例子:
lock = threading.Lock()
def get_first_part():
lock.acquire()
try:
... 从共享对象中获取第一部分数据
finally:
lock.release()
return data
def get_second_part():
lock.acquire()
try:
... 从共享对象中获取第二部分数据
finally:
lock.release()
return data
示例中,我们有一个共享资源,有两个分别取这个共享资源第一部分和第二部分的函数。两个访问函数都使用了锁来确保在获取数据时没有其它线程修改对应的共享数据。
现在,如果我们想添加第三个函数来获取两个部分的数据,我们将会陷入泥潭。一个简单的方法是依次调用这两个函数,然后返回结合的结果:
def get_both_parts():
first = get_first_part()
seconde = get_second_part()
return first, second
这里的问题是,如有某个线程在两个函数调用之间修改了共享资源,那么我们最终会得到不一致的数据。最明显的解决方法是在这个函数中也使用lock:
def get_both_parts():
lock.acquire()
try:
first = get_first_part()
seconde = get_second_part()
finally:
lock.release()
return first, second
然而,这是不可行的。里面的两个访问函数将会阻塞,因为外层语句已经占有了该锁。为了解决这个问题,你可以通过使用标记在访问函数中让外层语句释放锁,但这样容易失去控制并导致出错。幸运的是,threading模块包含了一个更加实用的锁实现:re-entrant锁。
Re-Entrant Locks (RLock)
RLock类是简单锁的另一个版本,它的特点在于,同一个锁对象只有在被其它的线程占有时尝试获取才会发生阻塞;而简单锁在同一个线程中同时只能被占有一次。如果当前线程已经占有了某个RLock锁对象,那么当前线程仍能再次获取到该RLock锁对象。
lock = threading.Lock()
lock.acquire()
lock.acquire() #: 这里将会阻塞
lock = threading.RLock()
lock.acquire()
lock.acquire() #: 这里不会发生阻塞
RLock的主要作用是解决嵌套访问共享资源的问题,就像前面描述的示例。要想解决前面示例中的问题,我们只需要将Lock换为RLock对象,这样嵌套调用也会OK.
lock = threading.RLock()
def get_first_part():
... see above
def get_second_part():
... see above
def get_both_parts():
... see above
这样既可以单独访问两部分数据也可以一次访问两部分数据而不会被锁阻塞或者获得不一致的数据。
注意RLock会追踪递归层级,因此记得在acquire后进行release操作。
Semaphores
信号量是一个更高级的锁机制。信号量内部有一个计数器而不像锁对象内部有锁标识,而且只有当占用信号量的线程数超过信号量时线程才阻塞。这允许了多个线程可以同时访问相同的代码区。
semaphore = threading.BoundedSemaphore()
semaphore.acquire() #: counter减小
... 访问共享资源
semaphore.release() #: counter增大
当信号量被获取的时候,计数器减小;当信号量被释放的时候,计数器增大。当获取信号量的时候,如果计数器值为0,则该进程将阻塞。当某一信号量被释放,counter值增加为1时,被阻塞的线程(如果有的话)中会有一个得以继续运行。
信号量通常被用来限制对容量有限的资源的访问,比如一个网络连接或者数据库服务器。在这类场景中,只需要将计数器初始化为最大值,信号量的实现将为你完成剩下的事情。
max_connections = 10
semaphore = threading.BoundedSemaphore(max_connections)
如果你不传任何初始化参数,计数器的值会被初始化为1.
Python的threading模块提供了两种信号量实现。Semaphore类提供了一个无限大小的信号量,你可以调用release任意次来增大计数器的值。为了避免错误出现,最好使用BoundedSemaphore类,这样当你调用release的次数大于acquire次数时程序会出错提醒。
线程同步
锁可以用在线程间的同步上。threading模块包含了一些用于线程间同步的类。
Events
一个事件是一个简单的同步对象,事件表示为一个内部标识(internal flag),线程等待这个标识被其它线程设定,或者自己设定、清除这个标识。
event = threading.Event()
#: 一个客户端线程等待flag被设定
event.wait()
#: 服务端线程设置或者清除flag
event.set()
event.clear()
一旦标识被设定,wait方法就不做任何处理(不会阻塞),当标识被清除时,wait将被阻塞直至其被重新设定。任意数量的线程可能会等待同一个事件。
Conditions
条件是事件对象的高级版本。条件表现为程序中的某种状态改变,线程可以等待给定条件或者条件发生的信号。
下面是一个简单的生产者/消费者实例。首先你需要创建一个条件对象:
#: 表示一个资源的附属项
condition = threading.Condition()
生产者线程在通知消费者线程有新生成资源之前需要获得条件:
#: 生产者线程
... 生产资源项
condition.acquire()
... 将资源项添加到资源中
condition.notify() #: 发出有可用资源的信号
condition.release()
消费者必须获取条件(以及相关联的锁),然后尝试从资源中获取资源项:
#: 消费者线程
condition.acquire()
while True:
...从资源中获取资源项
if item:
break
condition.wait() #: 休眠,直至有新的资源
condition.release()
... 处理资源
wait方法释放了锁,然后将当前线程阻塞,直到有其它线程调用了同一条件对象的notify或者notifyAll方法,然后又重新拿到锁。如果同时有多个线程在等待,那么notify方法只会唤醒其中的一个线程,而notifyAll则会唤醒全部线程。
为了避免在wait方法处阻塞,你可以传入一个超时参数,一个以秒为单位的浮点数。如果设置了超时参数,wait将会在指定时间返回,即使notify没被调用。一旦使用了超时,你必须检查资源来确定发生了什么。
注意,条件对象关联着一个锁,你必须在访问条件之前获取这个锁;同样的,你必须在完成对条件的访问时释放这个锁。在生产代码中,你应该使用try-finally或者with.
可以通过将锁对象作为条件构造函数的参数来让条件关联一个已经存在的锁,这可以实现多个条件公用一个资源:
lock = threading.RLock()
condition_1 = threading.Condition(lock)
condition_2 = threading.Condition(lock)
互斥锁同步
我们先来看一个例子:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time, threading
# 假定这是你的银行存款:
balance = 0
muxlock = threading.Lock()
def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
# 循环次数一旦多起来,最后的数字就变成非0
for i in range(100000):
change_it(n)
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t3 = threading.Thread(target=run_thread, args=(9,))
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print balance
结果 :
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
61
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
24
上面的例子引出了多线程编程的最常见问题:数据共享。当多个线程都修改某一个共享数据的时候,需要进行同步控制。
线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定/非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性。
threading模块中定义了Lock类,可以方便的处理锁定:
#创建锁mutex = threading.Lock()
#锁定mutex.acquire([timeout])
#释放mutex.release()
其中,锁定方法acquire可以有一个超时时间的可选参数timeout。如果设定了timeout,则在超时后通过返回值可以判断是否得到了锁,从而可以进行一些其他的处理。
使用互斥锁实现上面的例子的代码如下:
balance = 0
muxlock = threading.Lock()
def change_it(n):
# 获取锁,确保只有一个线程操作这个数
muxlock.acquire()
global balance
balance = balance + n
balance = balance - n
# 释放锁,给其他被阻塞的线程继续操作
muxlock.release()
def run_thread(n):
for i in range(10000):
change_it(n)
加锁后的结果,就能确保数据正确:
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
0
[/data/web/test_python]$ python multhread_threading.py
0
Ⅷ 小白学Python:Python从基础到深入(第2版)
使用网络网盘免费分享给你,链接:https://pan..com/s/1tQMIEGn7Vo53AyG_AIeHtg
Ⅸ 如何深入了解python原理
第一步阅读python所有内置模块的代码
第二步阅读python的C/C++源代码
Ⅹ python深入学习那个方向比较好
一、Python网络爬虫
学习目标
1.掌握Python网络爬虫基础及进阶
2.掌握基本的网络爬虫项目
3.编写复杂的爬虫项目
4.掌握反爬虫技术
二、Python人工智能
学习目标
1. 掌握Python机器学习与数据挖掘、深度学习基础
2. 学会用Python做数据处理
3. 掌握数据挖掘、机器学习与深度学习的核心知识点
4. 深入理解常见机器学习与数据挖掘算法的底层原理,并通过Python实现
5. 深入理解神经网络与深度学习算法的底层原理,并通过Python实现
6. 熟练使用深度学习的各种框架
7. 成为优秀的Python数据分析师
三、Python WEB开发
学习目标
1.了解Python网络开发知识
2.熟悉Python网络编程
3.为工作实战打下基础
四、Python自动化运维/测试
学习目标
1. 了解自动化运维/测试技术
2. 熟练使用Ansible、selenium等运维/测试工具
3. 熟悉掌握互联网企业运维/测试流程;能够自主搭建B/S自动化运维/测试平台
4. 通过Python实现对集群服务器进行批量自动化运维
5、自动化测试:接口自动化测试、web自动化测试等
6、辅助测试工具:性能测试工具、辅助测试工具等