本篇基于Python 2.7.9
根据廖雪峰Python教程整理
- URL:http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000
- 安装及IDE
上Python官网下载安装包。
配置环境变量:C:\Python27。
cmd中输入 python 出现信息则为安装正确。
Minor Problem:
用eclipse IDE 写Python时可能会出现输出中文(print “中文”)乱码的状况,这时候要在文件头加入
若是不想每次都更改,能够再Preferences-PyDev-Editor-Templates中的Empty中加入:
#-*- encoding: utf-8 -*-'''Created on ${date}@author: ${user}'''${cursor}
python做为更高级的语言,解释器是由C或者Java等其余语言实现的,因此在速度上要比较慢。
python中换行和缩进对齐是划分代码块一种方法,结尾没有分号。结尾若有冒号,则下面缩进的语句视为代码块。(缩进通常4个空格)
python源代码不能加密,发布既要发布源代码。
在CMD中也能够写Python,使用exit()退出。
注释使用#标记一行。使用'''@Notes'''能够标记多行。
大小写敏感,如bool型的True和False开头大写。
输出print:
加字符便可输出指定文字:
不一样字符能够用,链接。可是用,链接至关于中间增长一个空格:
不想增长空格,可使用:
-
print 'hello''world'print 'hello'+'world'
print自带换行,print就换一行,也可在字符中使用
\n。
若是不想换行,能够用:
print "hello",print "world"
可是这种状况自带空格了(+显然不能够),若是要求更高的自由度,可使用:(须要import sys)
sys.stdout.write("Hello World")
相似于java的system.out.print();
print中‘’和“”都是能够的。
固然Python也支持格式化输出:
print "Hi %s, your score is %d." %('Aran',99)
输入
raw_input():
调用raw_input()函数就至关于等待用户输入,能够用:
把结果存入name中。
其中raw_input()能够带参数,参数是字符串,至关于输入提示。返回的结构都是字符串类型。
顺便提下input()函数,它接受的对象是一个Python表达式,返回的是表达式的结果。
在name = input()后输入:
4==5
name的值是是bool型的False
python中可使用type(para)来获得para的数据类型。也能够用isinstance(para, int)来判断。
整型int
浮点型float
字符串型str:
是str不是String。
“”‘’嵌套是可使用转义符\。
使用r''表示''内字符不转义。
布尔型bool:
True、False
bool值得运算符有and、or、not三种运算
判断使用 ==
空值None:
内建类型None表示一个空对象,没有方法和属性。
None是一个特殊的常量。
None不是0。
None不是空字符串。
None和任何其余的数据类型比较永远返回False。
None有本身的数据类型NoneType。
你能够将None复制给任何变量,可是你不能建立其余NoneType对象。
变量:
大小写英文、数字和_的组合,且不能用数字开头。
深刻理解Python变量在内存中的实现(和Java相似):
当咱们写
a = 'ABC'
时,Python解释器是这么执行的:
1. 在内存中建立一个‘ABC’的字符串
2. 在内存中穿件一个名为a的变量,并指向‘ABC’
由此当咱们写
a = 'ABC'
b = a
a = 'XYZ'
时,系统会这样执行:
第一个赋值是指向。第二个赋值是将a的指向给予b。
理解这一点,对后面复杂函数对象的理解颇有意义。
ord()
函数将字符转换为ASCII码输出。
chr()
函数将ASCII码转换为字符输出。
list是一个可变的有序数据集。首元素从0开始。
name = ['Mike','Bob','Tracy']
python支持倒序。能够是从0~2,也能够是从-1~-3。超出范围后python会报IndexError错误。
list中数据类型不必定要相同。里面也能够继续使用list:
p = ['asp', 'php']s = ['python', 'java', p, 'scheme']print s[2][1]
固然也能够为空:L = []
经常使用函数有:
len():
能够获得list的元素个数:len(name)
append():
追加到list末尾:append('Adam')
insert(): 插入到某一索引:insert(1,'Jack')
pop():
删除某一位置的元素,没有参数则为最后一个元素:
pop() pop(1)
tuple:
tuple是一个不可变的有序数据集。
name = (
'Mike','Bob','Tracy'
)
其它的操做和list相似。只是定义一个元素的tuple时,为了和()区分开来,须要加逗号:name = ('Mike',)
重点解释下不可变:
是指tuple中元素的指向不会变化,
即指向'a',就不能改为指向'b',指向一个list,就不能改为指向其余对象,但指向的这个list自己是可变的!
看下面一段代码:
a = ['a1','a2']b = ['b1','b2']name = (a,b)a = ['A1','A2']b[0] = 'B1'b[1] = 'B2'print name
输出的结果为:
(['a1', 'a2'], ['B1', 'B2'])
这样理解:name中首元素指向了['a1','a2']的内存地址,虽而后来变量a指向了别处,但name中首元素所指向的内存地址中数据没有变化的。name中第二个元素指向了['b1','b2']的内存地址,可是后来该地址中的数据发生了变化,变成了['B1','B2']。全部name中就也有了变化。
相似于Java语言中的Map,使用key-value进行存储,是可变的无序不重复数据集。
举个栗子(名字—成绩):
dd = {'Mike':95, 'Bob':96, 'Jenny':92, 'Aran':99}
对于重复的key,后写入的key-value会覆盖前面的。
基于key来获得value:dd['Mike']。若是不存在该key则会报
KeyError的错误。
其余函数:
=:
赋值会覆盖掉原来key的value,若是key不存在,则至关于新增。
in:
判断一个元素是否在一个容器中,在后面的循环中会介绍。
get():
也是基于key来获得value的方法,若是不存在能够返回默认的None:dd.get('Thomas') 或本身定义的值: dd.get('Thomas','不存在')
BTW:
dict是根据key来计算value存储位置的,因此要求key不可变且不能相同。
与list相比,dict具备插入查找速度快的特色,相应的占用内存会多一些。
dict也有比较抽象的表达形式:{'Aran':90,'Bon':100,'Lee':98}['Aran'],这也是一个dict,'Aran'是索引,结果是90。
set:
set能够看做dict的简化版:只存储key而没有value。也能够看做list的严格版:只能存储无序的无重复的数据集。相似数学意义上的集合。
set是经过一个list进行构建的。
ss = set(['Allen','Bob','Crystal','David','Eclipse'])
add(key):
添加元素
remove(key): 删除元素
再议不可变之Hash:
dict和set容器都要求key值不可变不重复无序的,是由于他们的储存是要根据key值进行Hash的。因此它们要求元素或者其指向必定可以Hash,且容器中记录的永远是内存中的具体值。
栗子1:
a = ['a1','a2']b = ['b1','b2']ss = set([a,b])
会提示
TypeError,由于a、b是没法hash的。
栗子2:
b = ['b1','b2']ss = set(b) b[0] = 'B1'b[1] = 'B2'print ss
跟上面tuple的栗子同样,可是结果却不同:
set(['b1', 'b2'])
由于set直接记录的是具体的值,而tuple记录了指向。
if A: print Aelif B: print Belse: print 'others'
判断条件:
非零数值、非空字符串、非空list等,就判断为True,不然为False
BTW:
Python没有相似java的a>b?a:b三目运算符,可是它提供了xx if xx else xx.举例:
a if a>b else b 和java那个三目运算符同样。
循环:
for x in list/tuple/dict/set:
print x
把每一个元素(dict是key值)代入变量x,而后执行缩进块的语句。
函数range():
range(100):生成[0,100)这100个元素的list
range(5,99):生成[5,99)的list
举例数据类型转换函数:int(), float(), str(), unicode(), bool()
bool(2231) --> True
引用:
函数名其实就是一个函数对象的引用,
彻底能够把函数名赋给一个变量,至关于给这个函数起了一个“别名”。
xxx = bool
print xxx(214312) --> True
定义:
举绝对值函数例子:
def my_abs(x): if x >= 0: return x else: return -x
return后函数就结束了(废话)。若是没有写return,或者只写return,默认返回值是None
Python中能够return多个值:
def test(nx,ny):
return nx,ny
接受时:
x,y = text(1,2)
其实就是返回个tuple,python简化了。
BTW*2:
有些语句函数不知道写什么,能够先写个pass。
isinstance函数能够进行数据类型检查。
参数:
默认参数:
相似C++的默认参数(Java不支持,经过重载也能够达到相似效果):
def power(x,n=2):
若是只用一个参数,则n默认为2,若是有两个参数则n为第二个参数。
那么问题来了,多个默认参数,只想指定其中一个该怎么作:
def power(x,y,n=2,m=3):
调用时能够:power(1,2,m=9),从而n就是默认的2,m是9。
Att:
默认参数必须指向不变对象
,否则能够看下面的例子:
def add_end(L=[]): L.append('END') return L
至关于默认参数就是空List,在空List后面追加一个‘END’
执行print add_end() -->
['END']
结果对的。
再使用一次:print add_end() -->
['END', 'END'] 纳尼??
解释:
Python函数在定义的时候,默认参数L的值就被计算出来了,即[],由于默认参数L也是一个变量,它指向对象[],每次调用该函数,若是改变了L的内容,则下次调用时,默认参数的内容就变了,再也不是函数定义时的[]了。
至关于:
kk = []def add_end4(L = kk): L.append('END') return Lprint kkprint add_end4()print kkprint add_end4()print kk
kk的值一直在改变。
Ps~经测试当咱们的默认参数是固定字符串,set数据时,没有上述问题的。
可变参数:
可变参数容许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple
:
def calc(*numbers):
能够采用calc(1,2,3,4)的方式。
若是有一个list,也能够直接传list:
L = [1,2,3,4] calc(*L)
关键字参数:
而关键字参数容许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
def person(name, age, **kw):
能够传入任意个数的关键字参数:
>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
参数的组合运用:
上面的四种参数能够混合使用,注意参数定义的顺序是:
必选参数、默认参数、可变参数和关键字参数
。
看栗子:
def func(a, b, c=0, *args, **kw): print 'a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw
结果:
>>> func(1, 2)a = 1 b = 2 c = 0 args = () kw = {}>>> func(1, 2, c=3)a = 1 b = 2 c = 3 args = () kw = {}>>> func(1, 2, 3, 'a', 'b')a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}>>> func(1, 2, 3, 'a', 'b', x=99)a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
全部的参数均可以经过一个tuple和dict来传递:
>>> args = (1, 2, 3, 4)>>> kw = {'x': 99}>>> func(*args, **kw)a = 1 b = 2 c = 3 args = (4,) kw = {'x': 99}
参数总结:
*args是可变参数,args接收的是一个tuple;
**kw是关键字参数,kw接收的是一个dict。
可变参数既能够直接传入:func(1, 2, 3),又能够先组装list或tuple,再经过*args传入:func(*(1, 2, 3));
关键字参数既能够直接传入:func(a=1, b=2),又能够先组装dict,再经过**kw传入:func(**{'a': 1, 'b': 2})。
def func14(name, sex, age, job ='student', *args, **kwargs): return name,sex,age,job,args,kwargsfunc14('Aran','Male','23','studnet',*('java','C++'),**{'college':'USTC','major':'Software'})func14('Aran','Male','23','student','java','C++',college='USTC',major='Software')
上面代码中二者是同样的。
使用*args和**kw是Python的习惯写法,固然也能够用其余参数名,但最好使用习惯用法。
递归函数:
不要再return中才调用递归,这样会致使栈溢出。
切片:
能够很是方便的取得list和tuple的部分元素。
L是一个list,L[m:n]就是取得[m,n)位置的元素。(Python中常常前闭后开。)L[:]就是所有取。
Python支持倒序,L[-10:0]就是最后十个元素,并且0和最大位均可以省略。
Interesting的是Python支持间隔取数:L[::5]就是所有元素间隔5个取一个数。
迭代:
Python中for循环抽象程度要高于Java的for循环,它能够做用在任何能够迭代的对象上。
判断一个对象是否能够迭代,可使用:
isinstance(Object,Iterable) #返回bool
传统的List&Tuple遍历语法很简单:
def func18(): dd = {'a':1,'b':2,'c':3,'d':4,'e':5} for kk in dd: print kk, dd[kk] for vv in dd.itervalues(): print 'value: ',vv for kk,vv in dd.iteritems(): print kk,vv
其中直接遍历dict是获得key,遍历dict.itervalues()是获得value,遍历dict.iteritems()是获得一个tuple条目。
enumerate函数能够把list和tuple对象变成位置索引-数值对:
def func19(): tt = ('b','a','c','e','g') for i in enumerate(tt): print i
结果是:
(0, 'b')
(1, 'a')
(2, 'c')
(3, 'e')
(4, 'g')
上文中咱们也知晓,能够用两个变量来直接获得tuple的值。
列表生成式:
直接看例子:
- #2 & 3 Loop
def func20(): print [x*x for x in range(1,11)] print [x*x for x in range(1,11) if x%2 == 0] print [s.lower() for s in ['Hello','World','IBM','Apple']] print [m+n for m in 'ABC' for n in 'XYZ'] print [m+n+z for m in 'AB' for n in 'XY' for z in 'PQ']
结果:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
[4, 16, 36, 64, 100]
['hello', 'world', 'ibm', 'apple']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
['AXP', 'AXQ', 'AYP', 'AYQ', 'BXP', 'BXQ', 'BYP', 'BYQ']
以上例子在位于PythonLearn的2015-4-7.py。
生成器:
列表生成式在方便之余,还有一点小问题:若是建立一个几百万的大列表,而咱们只须要访问其中的几个元素,那就太浪费空间了。这时候咱们须要这种一边循环一边计算的机制,称为生成器(Generator)
g = (m+n+z for m in 'ABC' for n in 'XYZ' for z in '123')
与列表生成式相似,这样咱们定义了一个生成器。
获取里面的数值须要使用g.next()来获得下一个值。并且生成器会本身记录上次next到什么位置:(生成器也是能够迭代的)
print g.next() for n in g: print n
自定义生成器函数 - yield:
在函数中,函数是调用就顺序执行遇到return或者最后一行函数语句就返回。而在生成器函数中,yield代替了return,并且是在每次调用next()的时候执行函数,遇到yield语句返回,再次执行时是从上次返回的yield处继续执行。
举个栗子:
#Yield for Generatordef func5(): print 'Step 1' yield 1 print 'Step 2' yield 2 print 'Step 3' yield 3
直接调用函数是没有效果的。采用:
print func5().next()print func5().next()print func5().next()
输出结果:
Step 1
1
Step 1
1
Step 1
1
说明直接调用函数next()是没法保留yield记录的,须要将这个函数交给一个变量。
g= func5()print g.next()print g.next()print g.next()
这样就输出正确的结果了:
注意!不是g=func5,这个是指向这个函数的变量。
- 高阶函数(map/reduce, filter, sorted)
经过上文咱们已经明白变量能够指向函数(f=func),函数名也是变量在Python中的意义。这一意义使得回调函数变得异常简单。
#callback funcdef func6(x, y, f): return f(x)+f(y)
调用的话直接print func6(-10,-20,abs)。结果就是30。
map/reduce :
map函数接受两个参数,一个是函数,一个是序列,map将传入的函数依次做用到序列(不必定是list,只要可迭代)的每个元素,并把结果做为新的list返回。(返回必定是list)
举例将list中的每一个数字当作字符串返回:
print map(str,[11,22,33,44,55,66,77,88,99,100])
结果为:['11', '22', '33', '44', '55', '66', '77', '88', '99', '100']
reduce函数接受两个参数,一个是函数,一个是序列,reduce把函数做用在序列上的元素上,并和下一个元素作累积计算。其效果就至关于:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
举个栗子:
def fun8(x,y): return x*10+yprint reduce(fun8,[3,5,7,1,6,0,1,0])
结果为:35716010
上面咱们实现了intTostr,下面咱们实现strToint:(虽然这两个函数均可以用自带函数str()和int()直接实现)
def fun8(x,y): return x*10+ydef fun9(s): return {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}[s]def fun10(str): return reduce(fun8,map(fun9,str))print fun10('9231297'),type(fun10('9231297'))
结果为: 9231297 <type 'int'>
filter:
filter函数用于过滤序列。和map()相似,filter()也接受一个函数和一个序列。不过fliter()把传入的函数依次做用于每一个元素,而后根据返回值是True或False决定保留仍是丢弃该元素。
举个栗子:
def func11(n): return n%2 == 0print filter(func11,range(1,101))
就把[1,101)的偶数保留了下来。
完成了个素数保留的练习:
#Excerise Timedef primeJudege(n): if n == 1: return False elif n == 2: return True else: p = n/2 for i in range(2,p+1): if n%i == 0: return False return Trueprint filter(primeJudege,range(1,101))
sorted:
排序函数在python中也是抽象的高阶函数。
sorted函数默认从小到大排序:
print sorted([36, 5, 12, 9, 21]) ————> [5, 9, 12, 21, 36]
它能够接受一个函数,做为排序的依据:f(x,y)。函数必须返回-1做为依据:必需要返回-1来指明不调换的状况。(调换的状况能够不指明)若是没有返回-1,则不会对序列作操做。
如我重写教程中的例子:根据名字首字母从小到大排序,忽略大小写:
def func13(s1,s2): ss1 = s1.lower(); ss2 = s2.lower(); if ss1<ss2: return -1 else: return 1print sorted(['bob', 'about', 'Zoo', 'Credit'], func13)
结果:['about', 'bob', 'Credit', 'Zoo']
以上例子在位于PythonLearn的2015-4-8.py。
返回函数:
当咱们调用一个函数,但并不须要马上获得结果时,咱们能够把函数当作一个变量返回。在须要时再计算结果。
def func2(*args): def func2i1(): ans = 0 for n in args: ans = ans + n return ans return func2i1
在func2中定义了一个函数func2i1,它能够对func2函数的可变参数进行求和并返回结果。其实func2i1至关于一个闭包函数了(参见:
JS函数&闭包)。
而在fun2中,直接返回了函数。
f = func2(1,2,3,4,5)时,f还只是一个函数,令f()能够获得计算结果。
BTW:令f1 =
func2(1,2,3,4,5)且f2 = func2(1,2,3,4,5)。f1和f2并不等。
闭包:
闭包的属性咱们已经很清晰了:
def func3(): print 'inFunc3' def func3i1(): print 'InFunc3i1' print 'outFunc3'
在调用func3()时,闭包是不会执行了。除非func3()中再调用func3i1():
def func3(): print 'inFunc3' def func3i1(): print 'InFunc3i1' func3i1() print 'outFunc3'
这样就能够打印'InFunc3i1'了。理解了闭包是不会本身执行的,咱们看这个例子:
def func4(): l=[] for i in range(0,100): def func4i1(): l.append(i) func4i1() return l
func4i1虽然在循环中,可是能够理解为pass。循环结束后i=99,这时执行了一次func4i1(),因此l最后结果就是[99]
若是,把func4i1()加入循环中,就是[0,1,2,3.....99]。
理解了上述例子,咱们看教程中的例子:
def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fsprint [x() for x in count()]
append虽然在循环内,可是并无执行函数f(不是f()),因此理解成pass。当函数指向都添加(append)完毕了,x()执行函数时,i为3,返回9。因此答案是:
[9, 9, 9]
若是改为以下代码:
def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f()) return fsprint [x for x in count()]
答案就是:[1,4,9]
教程上采用了更牛逼的方法:再建立一个函数,用该函数的参数绑定循环变量当前的值,不管该循环变量后续如何更改,已绑定到函数参数的值不变。
def count(): fs = [] for i in range(1,4): def f(j): def g(): return j*j return g fs.append(f(i)) return fsprint [x() for x in count()]
至关于把f(1)、f(2)、f(3)放入list中。这样结果就没有什么异议了:
[1,4,9]
采用lambda关键字。冒号前面的x表示函数参数。匿名函数只能有一个表达式,不用写return,返回值就是该表达式的结果。
lambda x:x*x 就至关于 def f(x): return x*x
匿名函数能够没有参数,也能够当作返回值返回。
def build(x): return lambda: x
以上例子在位于PythonLearn的2015-4-9.py。
装饰器,简而言之就是给函数增长一些功能而不影响函数自己。
在Python能够方便的返回函数,装饰器的实现也比java简单不少。
假设有下面两个函数:
import datetimedef func1(mood): print mood , print datetime.date.today()def func2(name,score): print name,scorefunc1('happy')func2('Aran',99)
func1打印心情和日期。func2打印姓名和分数。
如今咱们想让调用函数的时候,打印出执行这个函数的名称,能够采用装饰器模式:
def func2c1(func): def wrapper(*args): print 'execute %s()....' %func.__name__ return func(*args) return wrapper
这是python中装饰器的格式,教程上的更加标准。
执行一下:
func2c1(func1)('happy')func2c1(func2)('Aran',99)
结果:
execute func1()....
happy 2015-04-10
execute func2()....
Aran 99
与此同时python还提供了一种更简单的方式:在函数定义前@装饰器,之后执行函数,至关于执行装饰器:
import datetimedef func2c1(func): def wrapper(*args): print 'execute %s()....' % func.__name__ return func(*args) return wrapper@func2c1def func1(mood): print mood, print datetime.date.today()@func2c1def func2(name, score): print name, scorefunc1('happy')func2('Aran', 99)
结果跟上面是同样的。(ps,函数定义上面能够加多个装饰器,多行@)
若是要让装饰器支持参数,须要将两重函数嵌套增长为三重:
def func2cc1(text): def decorator(func): def wrapper(*args): print '%s %s()...' %(text,func.__name__) func(*args) return wrapper return decorator@func2cc1('call')def func1(mood): print mood, print datetime.date.today()@func2cc1('call')def func2(name, score): print name, scorefunc1('happy')func2('Aran', 99)
把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
int()函数能够带一个参数,决定参数是多少进制来返回十进制。int('111',2)的结果是7。
若是咱们常用2进制,不想每次都输入参数,能够定义一个函数:
def int2(x, base=2): return int(x, base)
python也提供了更简单的偏函数方法:int2 = functools.partial(int, base=2)。和上面的函数是一个意思。
每个python文件都是一个模块。python的模块搜索路径包含当前目录,能够直接import。
(注意import不带.py)
Python中还有Java中包的概念,当前目录下须要import 包名.模块名。调用的时候也须要包名.模块名。(能够用别名来免去包名)
注意的是每个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,不然,Python就把这个目录当成普通目录,而不是一个包。__init__.py能够是空文件,也能够有Python代码,由于__init__.py自己就是一个模块,而它的模块名就是包名。对于这个模块,导入的时候只须要有import 包名
import sysdef func3(): args = sys.argv print 'Hello, your argv is:', print args[1:]
上面例子是使用命令行运行Python时,就能够看到带参数符了。
别名:
导入模块时还可使用别名。import aa as bb: 这样之后就要使用别名bb。
Tips:
Python标准库通常会提供StringIO和cStringIO两个库,这两个库的接口和功能是同样的,可是cStringIO是C写的,速度更快。因此常常会有这样的写法:
try: import cStringIO as StringIOexcept ImportError: # 导入失败会捕获到ImportError import StringIO
做用域:
正常的函数和变量名都是public的,能够直接引用。
相似__author__的是特殊变量(表示这个py的做者),能够被直接引用,但通常都是特殊用途。不要使用这种变量名。
相似_xxx或者__xxx的函数和变量是非公开的,不该该被直接引用。注意:不该该不是不能,python并无一种方法能够彻底限制访问private函数或变量,只是从编程习惯上不该该这么作。
安装第三方模块:
java中第三方依赖包管理能够用maven,而在python中封装了包管理工具:pip。(安装python时,确保勾选了pip和Add python.exe to Path。若是在cmd中输入pip没反应,就要从新运行安装程序添加pip)
第三方库的名称能够在官网的pypi上搜索,安装只须要在cmd中输入pip install xxx
模块搜索路径:
默认状况下,python解释器会搜索当前目录、全部已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中。能够把它打印出来查看。
咱们要添加本身的所搜目录,有两种方法:
一.直接append变量sys.path,运行时修改,运行结束后失效。
二.设置环境变量pythonpath,该环境变量的内容会被自动添加到模块搜索路径中。
__future__:
python的版本更迭速度比较快,要想在当前版本中使用新版的特性,须要使用__future__模块:
from __future__ import division
这样把新版本的除法特性移植过来了。
试一试不一样版本中除法:10/3, 10.0/3, 10//3
import module 和 from module import * :
后者能够在调用时省略
类与实例:
class Student(object): pass
class后面紧接着是类名,即Student,类名一般是大写开头的单词,紧接着是(object),表示该类是从哪一个类继承下来的,继承的概念咱们后面再讲,一般,若是没有合适的继承类,就使用object类,这是全部类最终都会继承的类。
定义好了Student类,就能够根据Student类建立出Student的实例,建立实例是经过类名+()实现的:
aran = Student()print aran
结果:<__main__.Student object at 0x10a67a590>。能够看到,变量bart指向的就是一个Student的object,后面的0x10a67a590是内存地址。
能够自由地给一个实例变量绑定属性:aran.name = 'aran'
因为类能够起到模板的做用,所以,能够在建立实例的时候,把一些咱们认为必须绑定的属性强制填写进去。经过定义一个特殊的__init__方法,在建立实例的时候,就把name,score等属性绑上去:
class Student(object): def __init__(self,name,score): self.name = name self.score = scorearan = Student('test',90)aran.name = 'aran'
注意到__init__方法的第一个参数永远是self,表示建立的实例自己,所以,在__init__方法内部,就能够把各类属性绑定到self,由于self就指向建立的实例自己。
ps:python类中的成员函数的第一个参数通常都是self。
以上例子在位于PythonLearn的2015-4-10.py。
数据封装:
类内能够直接定义函数,函数要包含一个参数就是self,相似初始化函数,这个参数在函数调用的时候不须要写。
前面的例子中,类的变量能够直接在外部进行访问赋值。咱们要想把它封装起来须要私有变量:__name,__score。这样就和java相似了,须要get/set方法,外部没法访问。
严格意义上,即便是私有变量python在外部也能够访问。python解释器只是替换了变量的名称,不过不要这么作。
继承:
跟Java继承差很少,定义类的时候()里面的是父类。
python中也有super()方法,不过和java不同,使用super(class,self)来得到class的父类。
多态:
子类能够覆盖父类的同名函数,和Java的多态差很少。
ps:采用instance函数判断一个子类的实例和父类,返回的结果是True。判断一个父类实例和子类,结果确定是False。
过于继承多态带来的的‘对扩展开放,对修改封闭’参见教程。
对象类型的判断:
type和isinstance前面已经讲过。其中isinstance参数支持tuple来判断是否是其中一个:
isinstance(u'a', (str, unicode))
dir(object):
返回object的全部属性和方法的list。
注意到字符串变量含有方法__len__。在Python中,调用len()其实是自动调用字符串的__len__()方法。
仅仅把属性方法列出来仍是不够的,python提供了getattr()、setattr()、hasattr()方法让咱们能够直接的操做一个对象。
getattr()是获得属性,getattr(obj, 'z'):获得obj对象名称为z的属性或者方法。
若是不存在会报
AttributeError的错误,
也能够设置成不存在返回默认值:getattr(obj,'z',404)
setattr须要三个参数:对象,属性,值。hasattr只须要两个,返回True或者False。
MethodTypes方法:动态增长成员函数
前面介绍了如何动态给类绑定属性(直接调用赋值)。位于types模块中的MethodTypes函数能够给类动态的增长成员函数。
给类的实例增长成员函数:
from types import MethodTypeclass Cla2(object): passdef set_name(self,name): self.name = namec = Cla2()c.ssname = MethodType(set_name,c,Cla2)c.ssname('Bom')print c.nameprint hasattr(c,'ssname')
三个参数分别是待绑定函数,实例名,类名。注意绑定到实例的成员函数是ssname而不是set_name。结果是:
Bom
True
给类增长成员函数:
from types import MethodTypeclass Cla2(object): passdef set_name(self,name): self.name = nameCla2.ssname = MethodType(set_name,None,Cla2)c = Cla2()c.ssname('Bom')print c.nameprint hasattr(c,'ssname')
结果和上面同样,注意中间的参数是None,也能够不写。
type():动态建立类
动态语言和静态语言最大的不一样,就是函数和类的定义,不是编译时定义的,而是运行时动态建立的。clas的定义是运行时动态建立的,而建立class的方法就是使用type()函数。咱们也能够经过type()函数建立类:
Cla4c = type('Cla4',(object,),dict(hello=func1))h =Cla4c()h.hello('aran')
以上代码等同于:
class Cla4(object): hello = func1Cla4c = Cla4h = Cla4c()h.hello('aran')
其中type传入的3个参数分别是:
class的名称;
继承的父类集合,注意Python支持多重继承,若是只有一个父类,别忘了tuple的单元素写法;
class的方法名称与函数绑定,这里咱们把函数fn绑定到方法名hello上。
ps, 经过type()函数建立的类和直接写class是彻底同样的,由于Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,而后调用type()函数建立出class。
metaclass:
直译为元类,咱们能够把类当作metaclass的‘实例’。有关metaclass的代码较为复杂且不太经常使用,在此忽略。
多重继承及Mixin:
多重继承就是在继承的基础上,多加一个类:Class Cla3(Cla1,Cla2):。(PS,Java是单一继承)
在设计类的继承关系时,一般,主线都是单一继承下来的,例如,Ostrich继承自Bird。可是,若是须要“混入”额外的功能,经过多重继承就能够实现,好比,让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计一般称之为Mixin(掺和模式)。
为了更好地看出继承关系,咱们把Runnable和Flyable改成RunnableMixin和FlyableMixin。相似的,你还能够定义出肉食动物CarnivorousMixin和植食动物HerbivoresMixin,让某个动物同时拥有好几个Mixin。
Mixin的目的就是给一个类增长多个功能,这样,在设计类的时候,咱们优先考虑经过多重继承来组合多个Mixin的功能,而不是设计多层次的复杂的继承关系。
在上文中咱们看到了类中__len__有着特殊用途。python中,还有一些特殊用途的属性函数和装饰器:
__slots__属性:
在class中,咱们能够经过__slots__来限制class可以添加的属性:
class Cla3(object): __slots__ = ('name','age')
这样这个类只能有这两个属性,不然会报AttributeError的错误。
ps,父类的__slot__属性子类默认不会继承。若是子类中也定义了__slots__,那么子
类容许定义的属性就是自身的__slots__加上父类的__slots__。
__str__:函数,返回类自己的print的结果。
__iter__:函数。若是一个类想要实现for...in的循环,就须要实现__iter__()方法。该方法返回一个迭代对象,而后Python的for循环就会不断地调用该迭代对象的next()方法拿到循环的下一个值,知道遇到StopIteration错误时退出循环。
举个例子,定制__iter__实现斐波那契数列迭代:
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self): return self # 实例自己就是迭代对象,故返回本身 def next(self): self.a, self.b = self.b, self.a + self.b # 计算下一个值 if self.a > 100000: # 退出循环的条件 raise StopIteration(); return self.a # 返回下一个值
__getitem__:函数。若是一个类想要按照下标取出元素,须要__getitem__实现,在上面类中加入以下函数:
def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a
可是此时Fib还不支持切片,由于__getitem__()传入的参数多是一个int,也多是一个切片对象slice,改进:
def __getitem__(self, n): if isinstance(n,int): a, b = 1, 1 for x in range(n): a, b = b, a + b return a if isinstance(n,slice): start = n.start stop = n.stop a,b = 1,1 L =[] for x in range(stop): if x >= start: L.append(a) a,b = b,a+b return L
可是这样的切片还不支持省略,可见实现一些基础功能并非so easy,利用三目运算符改进一下:
class Fib(object): def __getitem__(self, n): if isinstance(n,int): a,b = 1,1 for x in range(n): a,b = b,a+b return a if isinstance(n,slice): start = 0 if n.start == None else n.start stop = 100 if n.stop == None else n.stop a,b =1,1 L=[] for x in range(stop): if x>= start: L.append(a) a,b = b,a+b return Lff = Fib()print ff[:]
除了__getitem__外还有__setitem__():把对象视做list或者dict来对集合进行赋值。__delitem__():删除某个元素。在此再也不详述。
__getattr__:在调用类的成员变量函数中,若是类中没有这个属性,python默认就会再调用__getattr__()函数。(若是有的话不会)
class Cla1(object): @property def score(self): return self._score def __getattr__(self, item): if item == 'score': return 100 raise AttributeError("\'Cla1\' has no attribute: \'%s\'" %item)cc = Cla1()print cc.score
print的时候执行的就是__getattr__('score')
__call__:定义类的__call__方法能够直接执行实例自己的调用:
class Cla3(object): def __call__(self): print '__call__'dd = Cla3()dd()
执行的就是__call__函数。判断一个对象是否能够被调用,可使用callable(object)来判断。如上文中的cc返回False,dd返回True。
@property:设置默认赋值时调用函数的装饰器
在上面类的变量属性中,直接赋值而不检查参数,是不符合逻辑的。咱们采用get/set方法改进:
class Cla4(object): def get_score(self): return self._score def set_score(self,value): if not isinstance(value,int): raise ValueError('score must be an integer!') elif value <0 or value >100: raise ValueError('score must between 0 ~ 100!') else: self._score = value
参数不符合格式则抛出错误。(注意此时score是private,不然会出问题)
可是,咱们每次都调用get/set略显麻烦,此时须要python内置的@property装饰器来把一个方法变成属性调用直接赋值。如咱们想针对类中属性变量score进行装饰:
class Cla5(object): @property def score(self): return self._score @score.setter def score(self,value): if not isinstance(value,int): raise ValueError('score must be an integer!') elif value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') else: self._score = value
其中属性的get方法只用@property装饰,set方法须要score.setter装饰。
此后能够直接给调用获得score的值仍是赋值给score都是默认调用上面的方法。
以上例子在位于PythonLearn的2015-4-13.py。
python支持try...except...finally...,举例:
try: print 'try...' r = 10/0 print 'result:',rexcept ZeroDivisionError,e: print 'except:',efinally: print 'finally...'
当错误发生时,后续语句print 'result:', r不会被执行,except因为捕获到ZeroDivisionError,所以被执行。最后,finally语句被执行。而后,程序继续按照流程往下走。
python也支持多个except和else:
try: print 'try...' r = 10/int('a') print 'result:',rexcept ValueError,e: print 'ValueError',eexcept ZeroDivisionError,e: print 'ZeroDivisionError:',eelse: print 'no error!'finally: print 'finally...'
须要注意的是python的错误其实也是class,全部的错误类型都继承自BaseException,因此使用except时须要注意,它们不但捕获该类型的错误,还把其子类“一网打尽”。会致使子类永远捕获不到错误。
记录错误:
python运行出错后,咱们会在console中看到错误的层层堆栈信息,程序也终止运行。若是想让程序继续运行,可使用python内置的logging模块把错误堆栈打印出来,并让程序继续执行直至正常退出。看例子:
def foo(s): return 10 / int(s)def bar(s): return foo(s) * 2def main(): try: bar('0') except StandardError, e: logging.exception(e)main()print 'END'
console输出的结果:
C:\Python27\python.exe C:/Aran_Files/Git/PythonLearn/2015-04-14.pyERROR:root:integer division or modulo by zeroaranTraceback (most recent call last):aranEND File "C:/Aran_Files/Git/PythonLearn/2015-04-14.py", line 87, in main bar('0') File "C:/Aran_Files/Git/PythonLearn/2015-04-14.py", line 83, in bar return foo(s) * 2 File "C:/Aran_Files/Git/PythonLearn/2015-04-14.py", line 80, in foo return 10 / int(s)ZeroDivisionError: integer division or modulo by zeroProcess finished with exit code 0
以上例子在位于PythonLearn的2015-4-14.py。
抛出错误:
在实际代码编写中,咱们except到一个错误,打印错误信息后,能够直接raise(raise不带参数就把当前错误原样抛出)。由于捕获错误的目的只是记录一下,因为当前函数不知道应该怎么处理,最恰当的方式就是继续往上抛,以便顶层调用者去处理。
btw,咱们还能够except到一个错误类型,抛出另一个错误类型,只要是合理的转换逻辑便可。
print:
打印出来错误,简单粗暴有效。
assert:
能够用print的地方均可以用assert代替,assert后接一个表达式和一个字符串。若是表达式为假,则抛出
AssertionError并打印字符串,为真则没有异常。
def func2(s): assert s!=0,'n is zero' return sfunc2(0)
assert能够设置忽略,运行py时添加参数-O便可。
logging:
logging能够指定输出文件的位置,并且能够设置等级,根据等级来肯定哪些起做用。
logging的等级从低到高有:debug, info, warning, error。采用logging.info('')的方式。
设置级别的方式是:logging.basicConfig(level = logging.INFO)
ps,须要import logging
pdb.set_trace():设置断点
须要import pdb。程序会在pdb.set_trace()处暂停并进入pdb调试环境,使用‘p 变量名‘ 查看变量值,使用命令c继续运行。
IDE调试工具:
以上例子在位于PythonLearn的2015-4-20.py。
IO在计算机中值Input和Output。因为程序和运行时的数据都是在内存中由CPU来执行,涉及到数据交换的地方也就是磁盘、网络等,须要IO接口。
IO编程中,Stream(流)的概念很重要,Input Stream就是数据从外面(磁盘、网络)流进内存,Output Stream就是流出。每一个只能单向流动。
因为CPU和内存的速度远远高于外设,因此在IO编程中就有速度不匹配的问题,解决办法两个:
第一种是CPU等着,也就是程序暂停执行后续代码,等100M的数据在10秒后写入磁盘,再接着往下执行,这种模式称为同步IO;
另外一种方法是CPU不等待,只是告诉磁盘,“您老慢慢写,不着急,我接着干别的事去了”,因而,后续代码能够马上接着执行,这种模式称为异步IO。
同步IO和异步IO的区别在因而否等待IO执行结果,异步IO要比同步IO的效率更高,可是编程复杂度也会更高。由于,磁盘写入数据完毕时,如何通知CPU会有不一样的设计模式。
每一种编程语言都会把操做系统提供的低级C接口封装起来方便使用,Python也不例外,本章主要讨论同步IO。
读文件:
对于ASCII编码文件能够直接读:
f = open('Stream.txt','r')print f.read()f.close()
open的第一个参数是路径,默认是py当前文件夹,'r'表示只读。read()函数是一次性读取所有内容。最后要关闭stream。
按着java的逻辑,打开文件要trycatch确保文件不存在也不影响后续的操做:
try: f = open('Streasm.txt','r') print f.read()except IOError,e: passfinally: if f: f.close()
这样代码显得比较臃肿,python给咱们提供了with方法来替代上述代码:
with open('Stream.txt','r') as f: print f.read()
不用本身手动关闭Stream了。此外read()函数能够加参数肯定每次最多读取的字节数。readline()能够每次读取一行。readlines()能够一次性读取全部内容并按行返回list。
ps,文本中每一行都有'\n',能够经过line.strip()删掉。
对于非ASCII编码文件,须要以二进制模式打开,再编码:
with open('text.txt','rb') as f: print f.read().decode('gbk')
参数rb是打开二进制的方式,decode是按照编码格式解码。python以为每次read都要解码仍是很麻烦,提供了codecs快捷方法:
import codecswith codecs.open('text.txt','r','gbk') as f: print f.read()
写文件:
了解了读文件,写文件就比较简单了:
with open('Stream.txt','w') as f: f.write('Hello,world')
其中参数w、wb表示删除原文本写入(再次write时不会覆盖,由于是同一个f)。a表示在原文本上追加。
btw,写文件时操做系统都是先缓存数据,再写入磁盘,只有当stream close时才所有写入。so~最好使用with语句。
查看系统:
python内置的os模块能够直接调用系统提供的接口函数:
os.name:操做系统,若是是windows是nt
os.environ:环境变量
os.getenv():获得某个环境变量
操做文件和目录:
os.path.abspath('.'):获得当前目录下的绝对路径(也可使用sys.argv[0])
查看当前目录下的全部文件:(参数也可使用上面的那个绝对路径,而非'.')
for file in os.listdir('.'):
print file
过滤查看当前目录下文件:
for file in glob.glob('*.py'):
print file
路径拼接:(因为不一样操做系统路径表示不同,python提供方法省去操做麻烦)
os.path.join(os.path.abspath('.'),'testdir','test.txt')
结果:C:\Aran_Files\Git\PythonLearn\testdir\test.txt
建立删除路径:
os.mkdir()
os.rmdir()
拆分路径和文件:(把一个路径拆分为两部分,后一部分老是最后级别的目录或文件名):
os.path.split(sys.argv[0])
拆分文件拓展名:
os.path.spilitext(sys.argv[0])
重命名和删除文件:
os.rename()和os.remove()
拷贝文件的功能在os模块中并无提供,可使用shutil模块中额copyfile函数。
综上咱们能够用上面的组合实现一些快捷功能:
当前目录下全部子目录:[x for x in os.listdir('.') if os.path.isdir(x)]
当前目录下全部py文件:[x for x in os.listdir('.') if os.path.splitext(x)[1]=='.py']
练习:编写一个能在当前目录以及当前目录的全部子目录下查找文件名包含指定字符串的文件的函数:
def search(str,path=os.path.abspath('.')): for x in os.listdir(path): if os.path.isdir(x): search(str,os.path.join(path,x)) else: if str in x: print os.path.join(path,x)
变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling。把变量内容从序列化的对象从新读到内存里称之为反序列化,即unpickling。
Python提供cPickle模块和pickle模块来实现序列化pickle.dumps()和反序列化pickle.loads():
try: import CPickle as pickleexcept ImportError: import pickled = dict(name='Bob',age=20,score=88)dp = pickle.dumps(d)print dpd = pickle.loads(dp)print d
也有针对文件的pickle.dump()和pickle.load():
d = dict(name='Bob',age=20,score=88)with open('dump.txt','wb') as f: pickle.dump(d,f)with open('dump.txt','rb') as f: d = pickle.load(f)print d
上面序列化的格式只可以在python中使用显然不够通用,python内置了json模块来提供转换:
J
SON类型
Python类型
{}
dict
[]
list
"string"
'str'或u'unicode'
1234.56
int或float
true/false
True/False
null
None
采用json.dumps()和json.loads()来实现。
在序列化当中,默认形式都是以dict传输的。若是不是dict对象(好比类),咱们须要手动的将其转换为dict再进行序列化。好在一般类的实例中都有一个__dict__属性,它就是该类的一个dict:
json.dumps(s.__dict__)
有少数类除外,好比定义了__slots__的类,这种状况下只有本身手写转换函数了。dumps()支持不少参数,其中关键字参数
default
就是一个转换函数。
在反序列化中,咱们必须手动实现DictToClass的函数了,用于替代loads里面的object_hook参数:
import jsonclass Student(object): def __init__(self, name, score): self.name = name self.score = scores = Student('Bob', 88)#序列化print(json.dumps(s.__dict__))#反序列化print(json.loads(json.dumps(s.__dict__),object_hook=lambda d:Student(d['name'],d['score'])))
python既支持多进程又支持多线程。
多进程:
Linux下能够经过fork()调用实现多进程,详见教程。跨平台使用multiprocessing:
- 运行结果: Parent process 9220. Process will start. Run child process test (99)... Process end.
from multiprocessing import Processimport os
def run_proc(name): print 'Run child process %s (%s)...' % (name, os.getpid())
if __name__=='__main__': print 'Parent process %s.' % os.getpid() p = Process(target=run_proc, args=('test',)) print 'Process will start.' p.start() p.join() print 'Process end.'
run_proc就是一个打印函数,os.getpid是进程的pid。
if __name__ == '__main__'这个语句很常见,用来判断当前模块是直接运行的仍是import的。import进来的模块的__name__是模块名,缺省名称的当前模块下__name__为__main__。
Pro
cess()函数就是用来建立进程的构造函数,支持不少参数,能够查看Declaration。target传入的函数会在进程启动时执行,该函数的参数就是args传入的tuple。
start时才启动进程,join用于等待子进程结束后再继续往下运行。
要启动大量进程的话就考虑进程池:
from multiprocessing import Poolimport os,time,randomdef time_task(name): print 'Run task %s(%s)...' %(name,os.getpid()) start = time.time() time.sleep(random.random()*10) end = time.time() print 'Task %s runs %0.2f seconds.'%(name,(end-start))if __name__ == '__main__': print 'Parent process %s.' % os.getpid() p = Pool(4) for i in range(5): p.apply_async(time_task,args=(i,)) print 'Waiting for all subprocesses done...' p.close() p.join() print 'All subprocesses done.'运行结果: Parent process 11232. Waiting for all subprocesses done... Run task 0(10824)... Run task 1(8960)... Run task 2(8216)... Run task 3(7340)... Task 2 runs 0.24 seconds. Run task 4(8216)... Task 3 runs 2.15 seconds. Task 1 runs 5.85 seconds. Task 0 runs 8.99 seconds. Task 4 runs 9.88 seconds. All subprocesses done.
使用方法和上面的差很少。注意import的是Pool,并且Pool()中参数表示同时执行的进程数。Pool对象在调用join()后就开始执行并等待全部子进程执行完毕,调用join()前必须先调用close(),来让进程池不能添加新的进程了。
多进程就要考虑进程之间的通讯:Queue()类
from multiprocessing import Process, Queueimport 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: value = q.get(True) print 'Get %s from queue.' % valueif __name__=='__main__': # 父进程建立Queue,并传给各个子进程: q = Queue() pw = Process(target=write, args=(q,)) pr = Process(target=read, args=(q,)) # 启动子进程pw,写入: pw.start() # 启动子进程pr,读取: pr.start() # 等待pw结束: pw.join() # pr进程里是死循环,没法等待其结束,只能强行终止: pr.terminate()运行结果: Put A to queue... Get A from queue. Put B to queue... Get B from queue. Put C to queue... Get C from queue.
以上例子在位于PythonLearn的2015-4-23.py和2015-04-23_process.py。
多线程:
python提供了封装好的threading高级模块进行多线程开发。
每一个进程都有一个主线程:MainThread。建立线程方法:
print threading.current_thread().namedef namePrint(ID): print threading.current_thread().name , IDt = threading.Thread(target=namePrint, name = 'Threadtest',args=(1,))t.start()t.join()
结果: MainThread Threadtest 1
使用threading.current_thread().name获得线程名,若是本身没有指定线程名,python会以Thread-1方式命名。建立线程的参数和进程相同,在start()时执行线程。
线程锁:
多线程和多进程最大的不一样在于,多进程中,同一变量各自有一份备份在于每一个进程中,互相不影响。而多线程中,全部变量由全部线程共享,因此没有线程锁的机制内容很容易被改乱。(缘由详见教程)
使用锁的方法就是先定义锁,再得到锁,在try中执行修改,在finally中释放掉锁:
balance = 0lock = threading.Lock()def changeBalance(n): global balance balance = ndef run_thread(n): lock.acquire() try: changeBalance(n) finally: lock.release()t1 = threading.Thread(target=run_thread,args=(100,))t2 = threading.Thread(target=run_thread,args=(20,))t1.start()t2.start()t1.join()t2.join()print balance
第2句定义了锁,第5句表示变量是全局的那个变量,第9句得到锁而后执行变量改变再释放掉。
ps,因为python解释器有GIL全局锁的问题,在python的多线程中没法充分利用cpu的每一个核,多进程能够。
ThreadLocal:
进程本身的数据该如何传输存放呢?
一种方式是线程的数据就在线程中,不对外共享可见:
- 结果: Hello, Alice (in Thread-1) Hello, Bob (in Thread-2)
import threadingdef process_print(name): print 'Hello, %s (in %s)' % (name, threading.current_thread().name)def process_thread(name): process_print(name)t1 = threading.Thread(target= process_thread, args=('Alice',))t2 = threading.Thread(target= process_thread, args=('Bob',))t1.start()t2.start()t1.join()t2.join()
这样的想法最天然,可是实现起来有些不简练:线程的数据要看成参数在不一样的函数之间传输。(由于函数是共享的)
不经过传参的方法,就是定义全局的dict,将线程数据所有放入其中,这样的话数据至关于透明并且共享:
import threadingdict = {}def process_print(): print 'Hello, %s (in %s)' % (dict[threading.current_thread().name], threading.current_thread().name)def process_thread(name): dict[threading.current_thread().name] = name process_print()t1 = threading.Thread(target= process_thread, args=('Alice',))t2 = threading.Thread(target= process_thread, args=('Bob',))t1.start()t2.start()t1.join()t2.join()
python给咱们提供了另外一种办法,既能够简练又能够数据隐藏- ThreadLocal:
import threadinglocal = threading.local()def process_print(): print 'Hello, %s (in %s)' % (local.name, threading.current_thread().name)def process_thread(name,): local.name = name process_print()t1 = threading.Thread(target= process_thread, args=('Alice',))t2 = threading.Thread(target= process_thread, args=('Bob',))t1.start()t2.start()t1.join()t2.join()
结果都和第一个结果同样,这样的处理须要考虑不一样线程args和函数参数匹配的问题。
以上例子在位于PythonLearn的2015-4-27.py。
分布式进程:
教程讲的略粗糙,在这里略过,有空再深究。
\d
表示一个数字
\w
表示一个字母或者数字
\s
表示一个空格或者Tab符
*
表示任意个字符(包括0个)
+
表示至少一个字符
?
表示0个或者1个字符
{n}
表示n个字符
{n,m}
表示n-m个字符
举个栗子:
\d{3}\s+\d{3,8}
3个数字 空格
3-8个数字,表示任意个空格分隔开的带区号的电话号码
ps,特殊字符可使用 \特殊字符 进行转义。
更精准的匹配须要使用[]和-表示范围:
[0-9]
能够匹配一个0-9的数字
[0-9]+
能够匹配至少一个数字
[0-9a-zA-Z]
能够匹配一个数字或者字母
A|B
能够匹配A或者B
^
表示行的开头,^\d表示以数字为开头
$
表示行的结束,\d$表示以数字结束
正则表达式还能够设置分组,用()表示一个分组。
贪婪匹配:
正则分组默认的是前面贪婪匹配,若是咱们用(^\d+)(0*$)去匹配2312020000则第二个分组就匹配不到,咱们必须让第一个不进行贪婪匹配,加一个?就能够了:(^\d+?)(0*$)匹配获得的分组是231202,0000。
python中的正则表达式:
因为python字符串也须要\进行转义,因此特别注意\\: print 'ABC\\-001' 结果就是ABC\-001。加上r能够防止转义,详见下文。
re模块:
re模块提供match()函数,匹配则返回一个Match对象,不然返回None:(加r防止字符转义)
import retest = 'QQ1041149156's = r'^[Q|q]{2}\d*'if re.match(s,test): print Trueelse: print False
字符串切分:
普通的字符切分很简单:'a b c'.split(' ')输出切分后的list。若是复杂一点,a b c之间空格不定,则须要使用正则表达式进行切分:
test = 'a b c d's = r'\s+'print re.split(s,test)
分组:
正则匹配有分组的话,在python中也能够实现:
test = '091-23123123's = r'(^\d{3})-(\d{3,8}$)'m = re.match(s,test)print m.groups() 结果: ('091', '23123123')
编译:
python中使用正则表达式,re模块的执行顺序是:先编译正则表达式(若是不合法会报错),再去匹配。若是一个正则要重复使用,处于效率的考虑,能够预先编译,省去重复编译的环节:
test = 'qq1041149156's = r'^[Q|q]{2}\d*'re_s = re.compile(s)if re_s.match(test): print Trueelse: print False
namedtuple:能够给tuple添加属性名称,便于方便的引用:
from collections import namedtuplePoint = namedtuple('Point',['x','y'])p = Point(1,2)print p.x,p.y
deque:因为list是线性存储的,因此插入和删除显得比较麻烦,deque就是为了高效实现插入和删除操做的双向列表:append,appendleft,pop,popleft
from collections import dequeL = ['a','b','c']q = deque(L)q.append('y')q.appendleft('x')print q结果:deque(['x', 'a', 'b', 'c', 'y'])
defaultdict:使用dict时,若是引用的key不存在就会keyError,使用defaultdict则会返回一个默认值。
OrderedDict:实现dict按照key值插入的顺序排序
Counter:是一个简单的计数器,统计字符出现的个数:
from collections import Counterc= Counter()for ch in 'Programmer': c[ch] = c[ch] + 1print c结果:Counter({'r': 3, 'm': 2, 'a': 1, 'e': 1, 'g': 1, 'o': 1, 'P': 1})
Base64:
一种任意二进制到文本字符串的编码方式。在此略过。
struct:
pyhton中用来解决str和其它二进制数据类型的转换。在此略过。
hashlib:
python中的hashlib提供了常见的摘要算法,MD5和SHA1等等:
import hashlibmd5 = hashlib.md5()md5.update("My name is AranLiu.")print md5.hexdigest()
算法主要用于验证和防篡改,详情参见教程。
itertools:
python的内建模块提供了很是有用的用于操做迭代对象的函数。在此略过。
以上例子在位于PythonLearn的2015-4-28.py。
XML、HTMLParser、PIL和图形界面编程在此略过。
python基础知识点到此结束。
教程随后的项目及实战模块,会以单独笔记的形式进行一周一更。