随手记录--python基础知识

来源:互联网 发布:php工程师需要具备 编辑:程序博客网 时间:2024/05/20 17:24
Python
存储地址:F:\py2.7 F:\PY3.0


1、语句无须分号结束


2、输出print( ‘string’) / print( ‘ ’,‘ ’) / print(‘ ’,name),遇到逗号“,”会输出一个空格


3、输入input()


4、数据类型:整数(除法有两种:/两个整数恰好整除,结果是浮点数;//称为地板除,两个整数的除法仍然是整数)、浮点数、字符串(使用单引号或者双引号表示,若要表示单引号需要双引号括起来,如果既包含单引号和双引号需要转义字符,r’ ’表示‘’内部的字符串默认不转义,'''...'''表示多行内容)、布尔值(True /False注意大小写)、空值None


5、Python是动态语言,可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,用全部大写的变量名表示常量


6、len()函数计算的是str的字符数,如果换成bytes,len()函数就计算字节数


7、list。list是一种有序的集合,可以随时添加和删除其中的元素,用len()函数可以获得list元素的个数,用索引来访问list中每一个位置的元素,可以用-1做索引,直接获取最后一个元素,可以用append()往list中追加元素到末尾,可以用listname.insert(1,value)把元素插入到指定的位置,要删除list末尾的元素,用pop()方法,要删除指定位置的元素,用pop(i)方法,要把某个元素替换成别的元素,可以直接赋值给对应的索引位置,list里面的元素的数据类型也可以不同,也可以是另一个list,


8、tuple,一种有序列表,和list非常类似,但是tuple一旦初始化就不能修改,只有1个元素的tuple定义时必须加一个逗号,来消除歧义,tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向'a',就不能改成指向'b',指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的


9、条件判断。根据Python的缩进规则,如果if语句判断是True,就把缩进的语句执行,elif是else if的缩写,可以有多个elif:if <条件判断1>:
    <执行1>
elif <条件判断2>:
   <执行2>
elif <条件判断3>:
    <执行3>
else:
    <执行4>


10、Python的循环有两种,一种是for...in循环,依次把list或tuple中的每个元素迭代出来:for  tempvalue  in  ordervalue;第二种循环是while循环,只要条件满足,就不断循环,条件不满足时退出循环


11、Python提供一个range()函数,可以生成一个整数序列,再通过list()函数可以转换为list。比如range(5)生成的序列是从0开始小于5的整数


12、dict,全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度,在放进去的时候,必须根据key算出value的存放位置,这样,取的时候才能根据key直接拿到value,把数据放入dict的方法,除了初始化时指定外,还可以通过key放入,由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉,要避免key不存在的错误,有两种办法,一是通过in判断key是否存在,如:key in dict;通过dict提供的get方法,如果key不存在,可以返回None,或者自己指定的value,dict.get(‘key’)/dict.get(‘key’,-1),dict内部存放的顺序和key放入的顺序是没有关系的.


13、dict与list的对比:查找和插入的速度极快,不会随着key的增加而变慢;需要占用大量的内存,内存浪费多,dict是用空间来换取时间的一种方法。


14、set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。要创建一个set,需要提供一个list作为输入集合,var=set([1,2,3]),通过add(key)方法可以添加元素到set中,可以重复添加,但不会有效果,通过remove(key)方法可以删除元素,set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作.


15、set与dict的区别:set和dict的唯一区别仅在于没有存储对应的value,但是,set的原理和dict一样,所以,同样不可以放入可变对象,因为无法判断两个可变对象是否相等,也就无法保证set内部“不会有重复元素”


16、数据类型检查可以用内置函数isinstance()实现,if (not) isinstance(value,(int,float,...) )


17、函数。函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”。定义:在Python中,定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和冒号: ,然后,在缩进块中编写函数体,函数的返回值用return语句返回,如果没有return语句,函数执行完毕后也会返回结果,只是结果为None,return None可以简写为return。如:把my_abs()的函数定义保存为abstest.py文件,可以在该文件的当前目录下启动Python解释器,用from abstest import my_abs来导入my_abs()函数,注意abstest是文件名(不含.py扩展名),def  myabs (a):
If xxxx:
else:
如果想定义一个什么事也不做的空函数,可以用pass语句,pass可以用来作为占位符 def nop():
pass


18、函数可以多个返回值,在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便。


19、在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。


20、使用默认参数,var=n,设置默认参数时,默认参数必须指向不变对象!有几点要注意:


一是必选参数在前,默认参数在后,否则Python的解释器会报错;
二是如何设置默认参数。当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。有多个默认参数时,调用的时候,既可以按顺序提供默认参数,比如调用enroll('Bob', 'M', 7),意思是,除了name,gender这两个参数外,最后1个参数应用在参数age上,city参数由于没有提供,仍然使用默认值。也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需要把参数名写上。比如调用enroll('Adam', 'M', city='Tianjin'),意思是,city参数用传进去的值,其他默认参数继续使用默认值。
21、可变参数。常规方法:把参数作为一个list或tuple传进来,但是调用的时候,需要先组装出一个list或tuple,[ ]/();定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数。在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去


22、关键字参数,**kw是关键字参数,kw接收的是一个dict,关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过**kw传入:func(**{'a': 1, 'b': 2})。


23、函数小结:定义函数时,需要确定函数名和参数个数;如果有必要,可以先对参数的数据类型做检查;函数体内部可以用return随时返回函数结果;函数执行完毕也没有return语句时,自动return None;函数可以同时返回多个值,但其实就是一个tuple。


24、递归函数,递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。解决递归调用栈溢出的方法是通过尾递归优化,尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况,


25、切片。Python提供了切片(Slice)操作符,L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3,如果第一个索引是0,还可以省略,Python支持L[-1]取倒数第一个元素,那么它同样支持倒数切片:取前n个数slice[:n];去后n个数,slice[-n:];前n到m个数 slice[n,m];前n个数,每个m个取一个,slice[:n:m];所有数每n个取一个slice[::n];[:]可以原样复制一个list;tuple也是一种list,唯一区别是tuple不可变。因此,tuple也可以用切片操作,只是操作的结果仍是tuple:如(0, 1, 2, 3, 4, 5)[:3]。字符串'xxx'也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串。


26、迭代。在Python中,迭代是通过for ... in来完成的,因为dict的存储不是按照list的方式顺序排列,所以,迭代出的结果顺序很可能不一样,默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.values(),如果要同时迭代key和value,可以用for k, v in d.items()。字符串也是一种可迭代对象:for ch in 'ABC':我们不太关心该对象究竟是list还是其他数据类型,判断一个对象是可迭代对象通过collections模块的Iterable类型判断:from collections import Iterableisinstance(‘ ’,Iterable)/isinstance([  ],Iterable)/isinstance(123,Iterable)Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:for i, value in enumerate(['A', 'B', 'C'])。


27、列表生成式,列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。写列表生成式时,把要生成的元素x * x放到前面,后面跟for循环,就可以把list创建出来,for循环后面还可以加上if判断,这样我们就可以筛选出符合要求的,[for x in range( , )if  y],还可以使用两层循环,可以生成全排列,运用列表生成式,可以写出非常简洁的代码。例如,列出当前目录下的所有文件和目录名,可以通过一行代码实现:[d for d in os.listdir('.')] # os.listdir可以列出文件和目录, for循环其实可以同时使用两个甚至多个变量,比如dict的items()可以同时迭代key和value,因此,列表生成式也可以使用两个变量来生成list,如果list中既包含字符串,又包含整数,由于非字符串类型没有lower()方法,所以列表生成式会报错,受到内存限制,列表容量肯定是有限的。


28、在Python中,这种一边循环一边计算的机制,称为生成器:generator。要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:L = [x * x for x in range(10)]-->g = (x * x for x in range(10)),如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值,generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误,可以用for迭代;如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator,函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。


29、迭代器(Iterator),可以被next()函数调用并不断返回下一个值的对象。使用isinstance()判断一个对象是否是Iterable对象,from collections import Iterableisinstance(str/int/list/... ,Iterable)
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator,原因是Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。把list、dict、str等Iterable变成Iterator可以使用iter()函数:isinstance(iter([]/‘str’/int/...), Iterator)。


30、生成器迭代器小结:凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。Python的for循环本质上就是通过不断调用next()函数实现的


31、高阶函数,让函数的参数能够接收别的函数。
(1)map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。如: r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]),f是函数,r是一个Iterator,Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list。


(2)reduce的用法:reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:from functools import reducereduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)。


(3)filter()函数用于过滤序列,关键在于正确实现一个“筛选”函数。和map()类似,filter()也接收一个函数和一个序列,和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素,filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。filter()的作用是从一个序列中筛出符合条件的元素。由于filter()使用了惰性计算,所以只有在取filter()结果的时候,才会真正筛选并每次返回下一个筛出的元素


(4)Sorted,可以对list进行排序,可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序sorted([36, 5, -12, 9, -21], key=abs),key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。默认情况下,对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面,忽略大小写,按照字母序排序。要实现这个算法,不必对现有代码大加改动,只要我们能用一个key函数把字符串映射为忽略大小写排序即可。忽略大小写来比较两个字符串,实际上就是先把字符串都变成大写(或者都变成小写),再比较,sorted(['b', 'a', 'c', 'C'], key=str.lower),要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True,sorted(['b', 'a', 'c', 'C'], key=str.lower, reverse=True)。sorted()也是一个高阶函数。用sorted()排序的关键在于实现一个映射函数


(5)返回函数。除了可以接受函数作为参数外,还可以把函数作为结果值返回。我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。请再注意一点,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数,注意到返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。返回函数不要引用任何循环变量,或者后续会发生变化的变量,如果一定要引用循环变量方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变。


(6)匿名函数,关键字lambda表示匿名函数,冒号前面的x表示函数参数。匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数,同样,也可以把匿名函数作为返回值返回。


(7)装饰器,在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator),本质上,decorator就是一个返回函数的高阶函数。由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数,函数对象有一个__name__属性,var._name_可以拿到函数的名字


面向对象:


1、python用class保留字来定义一个类,类名的首字符要大写。当程序员需要创建的类型不能用简单类型来表示时,就需要定义类,然后利用定义的类创建对象。定义类示例:
class objectname(object):
pass 
Class 后面是类名objectname,(object)表明这个类继承自object类,


2、当一个对象被创建后,包含了三方面的特性,即对象的句柄、属性和方法。创建实例是通过类名+()实现的:
fruit = Fruit() 
fruit.grow()
fruit指向一个实例,调用会显示内存地址为OX000000,每个object的地址都不一样,而Student本身则是一个类,可以自由地给一个实例变量绑定属性,比如,给实例bart绑定一个name属性bart.name


3、类的方法也分为公有方法和私有方法。私有函数不能被该类之外的函数调用,私有的方法也不能被外部的类或函数调用。


4、如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,def __init__(self, name, score):
self.__name = name
self.__score = score
无法从外部访问实例变量.__name和实例变量.__score了,如果外部代码要获取name和score可以给Student类增加get_name和get_score、set_score这样的方法:
class Student(object):
 def get_name(self):
        return self.__name
    def get_score(self):
        return self.__score
def set_score(self, score):
        self.__score = score
需要注意的是,在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__score__这样的变量名


5、python使用函数”staticmethod()“或”@ staticmethod“指令的方法把普通的函数转换为静态方法。静态方法相当于全局函数。


6、python的构造函数名为__init__,析构函数名为__del__,由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑上去,如:
class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
*注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:
bart = Student('Bart Simpson', 59)


7、数据封装:可以直接在Student类的内部定义访问数据的函数,这样,就把“数据”给封装起来了。这些封装数据的函数是和Student类本身是关联起来的,我们称之为类的方法:
class Student(object):
def __init__(self, name, score):
def print_score(self):
要定义一个方法,除了第一个参数是self外,其他和普通函数一样。要调用一个方法,只需要在实例变量上直接调用,除了self不用传递,其他参数正常传入,封装的另一个好处是可以给Student类增加新的方法,比如get_grade,同样的,get_grade方法可以直接在实例变量上调用,不需要知道内部实现细节,


8、继承的使用方法(类似于java的继承与多态):
class Apple(Fruit):
     def …


9、当拿到一个对象的引用,若要知道这个对象的类型,可用type()来判断,若要判断class的类型,需要用isinstance(),如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法,类似__xxx__的属性和方法在Python中都是有特殊用途的,比如__len__方法返回长度。在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法,所以,下面的代码是等价的:_len(‘ABC’) 和 'ABC'.__len__().仅仅把属性和方法列出来是不够的,配合getattr()、setattr()以及hasattr(),我们可以直接操作一个对象的状态, obj = MyObject(),hasattr(obj, 'x') # 有属性'x'吗? setattr(obj, 'y', 19) # 设置一个属性'y',getattr(obj, 'y') # 获取属性'y',如果试图获取不存在的属性,会抛出AttributeError的错误,可以传入一个default参数,如果属性不存在,就返回默认值:getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404;也可以获得对象的方法:hasattr(obj, 'power') # 有属性'power'吗?fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn,fn指向obj.power


10、实例属性和类属性:由于Python是动态语言,根据类创建的实例可以任意绑定属性。给实例绑定属性的方法是通过实例变量,或者通过self变量,可以直接在class中定义属性,这种属性是类属性,归Student类所有:
class Student(object):
name = 'Student'
在编写程序的时候,千万不要把实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。


11、若要限制实例的属性,在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:
class name(object):
_slots_=(‘name’,’age’)
用tuple定义允许绑定的的属性名称,若放入没绑定的属性,将得到AttributeError的错误,使用_slots_定义的属性仅对当前类的实例起作用,对继承的子类无作用,若在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__


12、Python内置的@property装饰器就是负责把一个方法变成属性调用,把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作
13、Python允许多继承,Mxiln,MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。


14、Python自带的很多库也使用了MixIn。举个例子,Python自带了TCPServer和UDPServer这两类网络服务,而要同时服务多个用户就必须使用多进程或多线程模型,这两种模型由ForkingMixIn和ThreadingMixIn提供。通过组合,我们就可以创造出合适的服务来。


15、特殊的定制类:_slots_、_len_()、_str_()和__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的;_iter_():如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环;__getitem__,可取出指定下标的元素,__getitem__()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断isinstance(),如果把对象看成dict,__getitem__()的参数也可能是一个可以作key的object,例如str;与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素;Python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性,在没有找到属性的情况下,可以调用__getattr__,已有的属性,不会在__getattr__中查找(如果要写SDK,给每个URL对应的API都写一个方法,那得累死,而且,API一旦改动,SDK也要改。利用完全动态的__getattr__,我们可以写出一个链式调用)


16、文件读写:open(path,‘’)、read()、close()、


17、序列化


18、进程:multiprocessing模块提供了一个Process类来代表一个进程对象,创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步,如果要启动大量的子进程,可以用进程池的方式批量创建子进程,对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了。


19、子进程:很多时候,子进程并不是自身,而是一个外部进程。我们创建了子进程后,还需要控制子进程的输入和输出。subprocess模块可以让我们非常方便地启动一个子进程,然后控制其输入和输出。进程间通信:Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据


20、线程:.Python的线程是真正的Posix Thread,而不是模拟出来的线程。Python的标准库提供了两个模块:_thread和threading,_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行,由于任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程又可以启动新的线程,Python的threading模块有个current_thread()函数,它永远返回当前线程的实例。主线程实例的名字叫MainThread,子线程的名字在创建时指定,我们用LoopThread命名子线程。名字仅仅在打印时用来显示,完全没有其他意义,如果不起名字Python就自动给线程命名为Thread-1,Thread-2……多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。获得锁的线程用完后一定要释放锁,否则那些苦苦等待锁的线程将永远等待下去,成为死线程。所以我们用try...finally来确保锁一定会被释放。锁的好处就是确保了某段关键代码只能由一个线程从头到尾完整地执行,坏处当然也很多,首先是阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了。其次,由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁,导致多个线程全部挂起,既不能执行,也无法结束,只能靠操作系统强制终止。



0 0