Python对象
1 属性
Python对象的三个属性:
- 身份:对象的唯一身份标识,可以使用内建函数id()查看,可被认为是对象的内存地址;
- 类型:对象保存的类型,可以使用内建函数type()查看;
- 值:对象表示的数据像。
2 标准类型
- 数字:
- Integer整型:
- Boolean布尔型:
- Long integer长整型:
- Floating point real number浮点型:
- Complex number复数型:1+2j
- String字符串:’string’
- List列表:[1,2,’3′],元素个数和元素的值可以改变。
- Tuple元组:(‘robots’, 77, 93, ‘string’),元组不可以改变,可看作是只读的列表。
- Dictionary 字典:映射数据类型,由key-value构成,{‘country’:’China’,’sex’:’male’,’name’:’Jack’}
3 其他内建类型
- 类型:type(),所有对象的类型都是type(type(type(*)))
- Null对象: Null对象或者NoneType,值为None,None类型类似C语言void,所有对象都可进行布尔测试,空对象,值为0的对象或Null对象布尔值都为0;
- 文件:
- 集合/固定集合
- 函数/方法
- 模块
- 类
4 对象身份的比较
foo1=foo2=1.2
值为1.2的对象被创建,并将对象引用被赋值给foo1和foo2对象,所以foo1 is foo2和id(foo1) == id(foo2)都为True
5 标准类型的内建函数
- cmp(obj1,obj2):比较对象结果返回整型i,如果obj1<obj2 i<0,obj1>obj2 i>0,obj1==obj2 i=0
- repr(obj):同
obj
,返回对象的字符串表示; - str(obj):返回对象可读性较好的字符串表示
- type(obj):返回type对象
repr()和str()的区别:尽管str(),repr()和运算在特性和功能方面都非常相似,但str()则有所不同,str()致力于生成一个对象的可读性好的字符串表示,它的返回结果通常无法用于eval()求值,但很适合用于print语句输出。
对象值比较( type(num) == type.IntType )和对象身份(type(num) is type.IntType)比较,在运行时期,只有一个类型对象来表示整型类型,type(0)、type(42)、type(-100)都是同一个对象<type ‘int’>(types.IntType也是这个对象)
如果是同一个对象,就没必要去比较他们的值,故比较对象本身就是一个最好的方案:
即 if type(num) is type.IntType 或者 type(0)
isinstance()判断对象类型,本身可以接受元组参数,方便判断。
6 访问模型
以访问类型分类:
- 直接访问:数字
- 顺序访问:字符串、列表、元组
- 映射访问:字典
以标准类型分类:
- 数字:标量、不可改变、直接访问
- 字符串:标量、不可改变、顺序访问
- 列表:容器、可改变、顺序访问
- 元组:容器、不可改变、顺序访问
- 字典:容器、可改变、映射访问(Hash)
7 不支持的类型
- char或byte:长度为1的字符串;
- 指针:不支持,Python管理;
- int vs short vs long:用户无需关心,超时范围自动增长;
- float vs double:默认为双精度浮点类型,如果想要更精确,需要导入Decimal。
数字
1 删除引用
删除数字对象引用:del
2 整型表达范围
Python的长整型所表达的数值范围与机器内存相关,即可以表达很大的整型范围;
3 移位运算
在数字 x 上左移 y 比特得到 x * 2y.如9 << 2 = 36
4 除法
地板除:整数相除,舍去小时部分,保留整数
1 2 3 4 |
>>> 8/3 2 >>> 8.0/3 2.6666666666666665 |
引入//作为地板除
5 数值运算内建函数
- abs(num):返回绝对值;
- coerce(num1,num2):将num1和num2转换为统一类型,然后以一元组的形式返回:
-
12>>> coerce(1.0,2)(1.0, 2.0)
-
- divmod(num1,num2):除法取余的结合,返回一元组:
-
12>>> divmod(5,3)(1, 2)
-
- pow(num1,num2,mod=1):取num1的num2次方,如果提供mod参数,则计算计算再对mod进行取余计算
-
123456>>> pow(2,3)8>>> pow(2,3,3)2>>> pow(2,3,5)3
-
- round(flt,ndig=1):接受一个浮点型flt,对其四舍五入,保存ndig小数,如不提供ndig参数,则默认小数点后0位
-
1234>>> round(8.0/3)3.0>>> round(8.0/3,2)2.67
-
6 转换
- hex(num):将num转换为16进制,并以字符串形式返回
-
123456>>> hex(16)'0x10'>>> hex(24)'0x18'>>> hex(30)'0x1e'
-
- oct(num):将num转换为8进制,并以字符串形式返回;
- chr(num):将ASCII值的数字转为ASCII的字符,num范围为0~255;
- ord(num):接受一个ASCII或Unicode字符(长度为1的字符串),返回相应的ASCII值或Unicode值;
- unichr(num):接受Unicode码值,返回对应的Unicode字符,所接受的码值范围依赖于Python构建于UCS-2还是UCS-4。
序列:字符串、列表、元组
1 序列
操作符:in, not in, seq[index], seq[ind1,ind2],seq*expr,
步长索引:seq[::-1]”翻转”, seq[::2] “隔一个取一个”
利用None作为索引值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
>>> s='123456' >>> s[::-1] '654321' >>> s[::2] '135' >>> for i in [None]+range(-1,-len(s),-1): ... print s[:i] ... 123456 12345 1234 123 12 1 |
类型转换的内建函数
list(iter):
str(obj): 把对象打印输出时非常有用。
unicode(obj): str()的unicode版
basestring(): 抽象工厂函数,仅为str()和unicode()函数提供父类,不能被实例化,也不能被调用
tuple(iter): 把一个可迭代的对象换成一元组对象
浅拷贝:只拷贝对对象的索引,而不是重新建立一个对象
深拷贝:完全拷贝一个对象(包括递归,如果你的对象是一个包含在容器中的容器)
可操作的内建函数:
enumerate(iter):接受一个可迭代的对象作为参数,返回一个enumerate对象(同时一个迭代器),该对象生成由iter每个元素的index值和item值组成的元组
len(seq): 返回seq的长度
max(iter, key=None):
max(agr0,arg1…key=None):
min(iter, key=None):
min(agr0,arg1…key=None):
reversed(seq):
sorted(iter,func=None,key=None,reverse=False): 接受一个序列作为参数,返回一个有序的列表
sum(seq,init=0): 返回seq和可选参数init的总和,效果等同于reduce
zip(it0,it1,…,itN): 返回一个列表,其第一个元素为it0,it1,…,itN的第一个元素组成的元组,第二个……依次类推。
2 字符串
转义字符串只在双引号字符串中其中用,单引号括起来的字符串中不起作用
1 2 3 4 5 |
>>> s=str(range(4)) >>> for i in s: ... print i, ... [ 0 , 1 , 2 , 3 ] |
反向索引:从-1开始,向字符串开始的反向计数,最后一个数为-len(seq)。
1 2 3 4 5 6 7 8 9 10 11 |
>>> s='123456' >>> s[-1] '6' >>> s[-2] '5' >>> s[-3] '4' >>> s[-3:] '456' >>> s[:-3] '123' |
成员操作符:
in,not in:判断包含关系
string模块预定义的字符串:
1 2 3 4 5 6 7 8 9 |
>>> import string >>> string.uppercase 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' >>> string.lowercase 'abcdefghijklmnopqrstuvwxyz' >>> string.letters 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' >>> string.digits '0123456789' |
其他函数
1 2 3 4 5 6 7 8 9 10 |
import string s = "Monty Python's Flying Circus" print s.upper() print s.lower() print s.split() print s.join(s.split()) print s.replace('Python', 'Shell') print s.find('Python'), s.find('Shell') print s.count('n') |
结果:
1 2 3 4 5 6 7 |
MONTY PYTHON'S FLYING CIRCUS monty python's flying circus ['Monty', "Python's", 'Flying', 'Circus'] MontyMonty Python's Flying CircusPython'sMonty Python's Flying CircusFlyingMonty Python's Flying CircusCircus Monty Shell's Flying Circus 6 -1 3 |
检测字符串是否可以作为用户id小脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#!/usr/bin/env python import string # # check whether the id string is legal alphas = string.letters + '_' nums = string.digits alphasnums = alphas + nums print 'Welcome to the Identifier Checker v1.0' print 'Testees must be at least 2 chars long.' myInput = raw_input('Please input a string to test?') if len(myInput) > 1: if myInput[0] not in alphas: print '''invalid: first symbol must be alphabetic''' else: for otherChar in myInput[1:]: if otherChar not in alphasnums: print '''invalid: remaining symbols must be alphanumeric''' break print 'Okey as an identifier' else: print 'Input not be at least 2 chars long' |
备注:从性能方面考虑,尽量不要把重复操作作为参数放到循环里面(while i < len(string))。处于性能方面考虑,不建议使用string模块,使用String模块。
字符串格式化输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
%s 字符串 (采用str()的显示) %r 字符串 (采用repr()的显示) %c 单个字符 %b 二进制整数 %d 十进制整数 %i 十进制整数 %o 八进制整数 %x 十六进制整数 %e 指数 (基底写为e) %E 指数 (基底写为E) %f 浮点数 %F 浮点数,与上相同 %g 指数(e)或浮点数 (根据显示长度) %G 指数(E)或浮点数 (根据显示长度) %% 字符"%" |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
>>> '%x' % 108 '6c' >>> '%X' % 108 '6C' >>> '%#X' % 108 '0X6C' >>> '%#x' % 108 '0x6c' >>> '%f' % 123.4567890 '123.456789' >>> '%.2f' % 123.4567890 '123.46' >>> '%E' % 123.4567890 '1.234568E+02' >>> '%g' % 123.4567890 '123.457' >>> '%G' % 123.4567890 '123.457' >>> '%.4G' % 123.4567890 '123.5' >>> '%.4g' % 123.4567890 '123.5' >>> '%.9g' % 123.4567890 '123.456789' >>> '%.10G' % 123.4567890 '123.456789' >>> '%.100G' % 123.4567890 '123.4567890000000005557012627832591533660888671875' >>> '%.20G' % 123.4567890 '123.45678900000000056' |
3 列表
列表可以使用append()方法来追加。
可以利用del删除列表元素或者列表本身。
标准操作符:列表比较利用内建的cmp()函数,比较元素,直到有一方的元素胜出。
可以在列表切片的基础上再进行切片,如:
1 2 3 4 5 |
>>> a=['a',[1,2,[3]]] >>> a ['a', [1, 2, [3]]] >>> a[1][2][0] 3 |
成员关系:in, not in
连接操作符+,必须同类型的才可以连接,不然会报错:
1 2 3 4 5 6 |
>>> a + 'a' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can only concatenate list (not "str") to list >>> a + ['b'] ['a', [1, 2, [3]], 'b'] |
可以使用extend()方法连接,区别+操作符是新建一个新的列表,list.extend()是把列表加到原有列表里面。
重复操作符*:
1 2 3 4 |
>>> a * 3 ['a', [1, 2, [3]], 'a', [1, 2, [3]], 'a', [1, 2, [3]]] >>> '-' * 20 '--------------------' |
sorted()和reversed(),排序是ASCII码值的字典序,不是字母序。
enumerate()和zip():
1 2 3 4 5 6 7 8 9 10 11 |
>>> a = ['a','b','c','d'] >>> for i,char in enumerate(a): ... print i, char ... 0 a 1 b 2 c 3 d >>> b=[1,2,3] >>> zip(a,b) [('a', 1), ('b', 2), ('c', 3)] |
list()和tuple(): 都可以接受迭代对象作为参数,并通过浅拷贝数据来创建一个新的列表和元组,常用于两种类型的转换。
1 2 3 4 5 6 7 8 9 |
>>> a ['a', 'b', 'c', 'd'] >>> tuple(a) ('a', 'b', 'c', 'd') >>> b=tuple(a) >>> b ('a', 'b', 'c', 'd') >>> list(b)==a True |
查看list的内建函数:
1 2 |
>>> dir(list) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] |
sort()、extend()和reverse()这些操作会改变现有的内容。
堆栈例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#!/usr/bin/env python # # use list and it's method to realize a stack stack = [] def pushit(): stack.append(raw_input('Enter New String: ').strip()) def popit(): if len(stack) == 0: print 'Cannot pop from an empty stack!' else: print 'Removed [',`stack.pop()`,']' def viewstack(): print stack CMDs = { 'u':pushit, 'o':popit, 'v':viewstack } def showmenu(): pr = """ p(U)sh p(O)p (V)iew (Q)uit Enter choice: """ while True: while True: try: choice = raw_input(pr).strip()[0].lower() except (EOFError, KeyboardInterrupt, IndexError): choice = 'q' print '\nYour picked: [%s]' % choice if choice not in 'uovq': print 'Invlid Option, try again' break if choice == 'q': break CMDs[choice]() if __name__ == '__main__': showmenu() |
队列的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#!/usr/bin/env python # # use list and it's method to realize a queue queue = [] def enQ(): queue.append(raw_input('Enter New String: ').strip()) def deQ(): if len(queue) == 0: print 'Cannot pop from an empty queue!' else: print 'Removed [',`queue.pop(0)`, ']' def viewQ(): print queue CMDs = { 'e':enQ, 'd':deQ, 'v':viewQ } def showmenu(): pr = """ (E)nquue (D)equeue (V)iew (Q)uit Enter choice: """ while True: while True: try: choice = raw_input(pr).strip()[0].lower() except (EOFError, KeyboardInterrupt, IndexError): choice = 'q' print '\nYour picked: [%s]' % choice if choice not in 'edvq': print 'Invlid Option, try again' break if choice == 'q': break CMDs[choice]() if __name__ == '__main__': showmenu() |
4 元组
一种不可变类型,意味着一旦一个对象被创建了,它的值就不能再被更新,除非重现创建一个新的对象。
不可变并非坏事,比如我们把数据传给一个不了解的API时,可以确保数据不被修改。
元组比较
1 2 3 4 5 6 7 8 |
>>> (4,2) < (3,5) False >>> (2,4) < (3,5) True >>> (2,4) == (3,-1) False >>> (2,4) == (2,4) True |
并非完全不可变:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
>>> t=('a','b') >>> t ('a', 'b') >>> t=t+('c','d') >>> t ('a', 'b', 'c', 'd') >>> t =(['xyz',123],23,-103.4) >>> t (['xyz', 123], 23, -103.4) >>> t[0][1] 123 >>> t[0][1]=['bdc','def'] >>> t (['xyz', ['bdc', 'def']], 23, -103.4) >>> t[0][1] ['bdc', 'def'] |
list()和tuple()函数允许列表和元组之间的转换。
对象拷贝:浅拷贝和深拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
>>> person = ['name',['saving',100.00]] >>> hubby = person[:] #slice copy >>> wifey = list(person) #fac func copy >>> [id(x) for x in person,hubby,wifey] [140145290337096, 140145290337240, 140145290337600] >>> hubby[0] = 'joe' >>> wifey[0] = 'jane' >>> hubby,wifey (['joe', ['saving', 100.0]], ['jane', ['saving', 100.0]]) >>> hubby[1][1] = 59.00 >>> hubby,wifey (['joe', ['saving', 59.0]], ['jane', ['saving', 59.0]]) >>> wifey[1][1] = 69.00 >>> hubby,wifey (['joe', ['saving', 69.0]], ['jane', ['saving', 69.0]]) >>> person ['name', ['saving', 69.0]] |
对一个对象进行浅拷贝,其实是新创建了一个类型跟援对象一样,内容是原对象的元素引用,即对象是新的,但内容不是。序列对象的浅拷贝是默认类型拷贝,通过以下方式进行:
- 完全切片操作[:]
- 利用工厂函数,比如list() 、dict()等
- 利用copy模块的copy()函数
上例中,wifey对象名称被赋值时,hubby的名称未发生改变,原因是第一个元素是不可变的字符串类型,第二个元素是个可变的列表。在拷贝时,字符串被显式拷贝,并创建一个字符串对象,但列表元素只是把他的引用复制一下,并非它的成员,故改变名称时没有问题,但改变列表元素时,会随之改变。可以通过id()查看改变前后,字符串元素id发生了改变,但列表的id并未发生改变。
对一个对象的深拷贝,即创建了一个新的容器对象,包含原有对象元素(引用)的全新拷贝的引用,需要使用copy.deepcopy()函数,重写上例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
>>> person = ['name',['saving',100.00]] >>> hubby = person >>> import copy >>> wifey = copy.deepcopy(person) >>> [id(x) for x in person,hubby,wifey] [140145290337312, 140145290337312, 140145290337384] >>> hubby[0] = 'joe' >>> wifey[0] = 'jane' >>> hubby,wifey (['joe', ['saving', 100.0]], ['jane', ['saving', 100.0]]) >>> hubby[1][1] = 59.00 >>> hubby,wifey (['joe', ['saving', 59.0]], ['jane', ['saving', 100.0]]) >>> [id(x) for x in hubby] [140145290375416, 140145290336808] >>> [id(x) for x in wifey] [140145290373328, 140145290385384] |
深拷贝时,列表元素的并未发生改变,通过id()查看两个对象的元素是不同的。
映射和类型集合
1 字典
1 2 3 4 5 6 |
>>> dict1={} >>> dict2 = {'name':'earth','port':80} >>> dict2 {'name': 'earth', 'port': 80} >>> dict1,dict2 ({}, {'name': 'earth', 'port': 80}) |
可以利用dict()来创建字典:
1 2 3 |
>>> ddict = dict((['x',1],['y',2])) >>> ddict {'y': 2, 'x': 1} |
可以利用fromkeys()来创建一个“默认”字典
1 2 3 |
>>> fdict = {}.fromkeys(('x','y'),-1) >>> fdict {'y': -1, 'x': -1} |
访问字典中的值,直接访问,或利用key()方法或in循环访问
1 2 3 4 5 6 7 8 9 10 11 12 |
>>> ddict['x'] 1 >>> for key in ddict.keys(): ... print 'key=%s, value=%s' % (key,ddict[key]) ... key=y, value=2 key=x, value=1 >>> for key in ddict: ... print 'key=%s, value=%s' % (key,ddict[key]) ... key=y, value=2 key=x, value=1 |
访问、更新与删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
>>> ddict['x']=100 >>> ddict['x'] 100 >>> del ddict['x'] >>> ddict['x'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'x' >>> ddict {'y': 2} >>> del ddict >>> ddict Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'ddict' is not defined |
in和not in检查某个键是否存在字典中
1 2 3 4 5 6 |
>>> fdict {'y': -1, 'x': -1} >>> 'y' in fdict True >>> 'y' not in fdict False |
字典比较算法,cmp()比较顺序如下:
- 比较字典长度,前者长度大,返回正值,反之为复制;
- 长度相同时,比较字典键,键的比较顺序与keys()方法返回键的顺序相同,当前者第一个不同键大于后者的第一个不同键时,cmp()返回正值;
- 字典长度并且键值完全匹配时,则比较相同键所对应的值,一档出现不匹配的值,就比较值,如果前者的值大与后者,返回正值;
- 完全匹配时,返回0。
看下面具体例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
>>> dict1={} >>> dict2 = {'name':'earth', 'port':'80'} >>> cmp(dict1,dict2) -1 >>> dict1 = {'name':'earth'} >>> cmp(dict1,dict2) -1 >>> dict1['port']='80' >>> cmp(dict1,dict2) 0 >>> dict1['port']='tcp' >>> cmp(dict1,dict2) 1 >>> cmp(dict1,dict2) 1 >>> dict1['port']='70' >>> cmp(dict1,dict2) -1 |
映射类型相关函数
- dict()
1 2 3 4 5 6 7 8 9 10 11 |
>>> zip(('x', 'y'),(1, 2)) [('x', 1), ('y', 2)] >>> dict(zip(('x', 'y'),(1, 2))) {'y': 2, 'x': 1} >>> dict1 {'name': 'earth', 'port': '70'} >>> dict3 = dict(dict1) >>> dict3 {'name': 'earth', 'port': '70'} >>> dict(x=1,y=3) {'y': 3, 'x': 1} |
- len()
1 2 3 4 |
>>> dict4 {'name': 'earth', 'port': '70'} >>> len(dict4) 2 |
- hash()
函数本身不是为字典设计,主要是判断摸个udoxiang是否可以作为一个字典的键,结果返回对象的哈希值,只有对象可哈希,才可作为字典的键,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
>>> dict1 {'name': 'earth', 'port': '70'} >>> hash(()) 3527539 >>> dict1 {(): 'null', 'name': 'earth', 'port': '70'} >>> hash([]) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list' >>> dict1[[]]='null' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list' >>> hash('') 0 >>> dict1['']='null' >>> dict1 {'': 'null', 'name': 'earth', 'port': '70'} |
映射类型的内建方法
- key():返回一个包含字典中键的列表
1 2 |
>>> dict1.keys() ['', (), 'name', 'port'] |
- clear():删除字典中所有元素
- fromkeys(seq, val=None):创建并返回一个新字典,以 seq 中的元素做该字典的键,val 做该字典中所有键对应的初始值(如果不提供此值,则默认为 None
1 2 3 4 5 6 7 8 |
>>> dict4.fromkeys('s') {'s': None} >>> dict4 {'name': 'earth', 'port': '70'} >>> dict4.fromkeys('s','hello') {'s': 'hello'} >>> dict4 {'name': 'earth', 'port': '70'} |
- copy():返回字典(浅复制)的一个副本
1 2 3 4 |
>>> dict3 {'name': 'earth', 'port': '70'} >>> dict4 = dict3.copy() >>> dict4 {'name': 'earth', 'port': '70'} |
- get(key):对字典 dict 中的键 key,返回它对应的值 value,如果字典中不存在此键,则返回 default 的值(注意,参数 default 的默认值为 None)。
1 2 3 4 5 6 7 |
>>> dict4 {'name': 'earth', 'port': '70'} >>> dict4.get('name') 'earth' >>> dict4.get('addr') >>> a=dict4.get('addr') >>> a |
- items():返回一个包含字典中(键, 值)对元组的列表
1 2 |
>>> dict4.items() [('name', 'earth'), ('port', '70')] |
- values() :返回一个包含字典中所有值的列表
1 2 |
>>> dict4.values() ['earth', '70'] |
- pop(key[, default]):和方法 get()相似,如果字典中 key 键存在,删除并返回 dict[key],如果 key 键不存在,且没有给出 default 的值,引发 KeyError 异常
1 2 3 4 5 6 7 8 |
>>> dict4.pop('') Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: '' >>> dict4.pop('name') 'earth' >>> dict4 {'port': '70'} |
- setdefault(key, default=None):和方法 set()相似,如果字典中不存在 key 键,由 dict[key]=default 为它赋值
- update(dict2) 将字典 dict2 的键-值对添加到字典 dict
1 2 3 4 5 6 7 8 9 |
>>> dict4 {'port': '70'} >>> dict3 {'name': 'earth', 'port': '70'} >>> dict4.update(dict3) >>> dict3 {'name': 'earth', 'port': '70'} >>> dict4 {'name': 'earth', 'port': '70'} |
注意:字典中键不允许一个键对应多个值,并且键是可哈希的。
2 集合
set是不用元素组成对集合,集合的成员称为集合对象,是一组无序排列的可哈希值。
工厂方法set()和frozenset():set是可变的,有add(),remove()等方法。既然是可变的,所以它不存在哈希值。
frozenset是冻结的集合,它是不可变的,存在哈希值,好处是它可以作为字典的key,也可以作为其它集合的元素。缺点是一旦创建便不能更改,没有add,remove方法。
1 2 3 4 5 6 |
>>> s = set('China') >>> s set(['i', 'h', 'C', 'a', 'n']) >>> t = frozenset('ZhongGuo') >>> t frozenset(['g', 'G', 'h', 'o', 'n', 'u', 'Z']) |
元素访问
1 2 3 4 5 6 7 8 |
>>> for i in t: ... print i, ... g G h o n u Z >>> 's' not in t True >>> 'G' not in t False |
更新与删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
>>> t.add('w') Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'frozenset' object has no attribute 'add' >>> t frozenset(['g', 'G', 'h', 'o', 'n', 'u', 'Z']) >>> s set(['i', 'h', 'C', 'a', 'n']) >>> s.add('W') >>> s set(['a', 'C', 'i', 'h', 'n', 'W']) >>> s.update('Whoispower') >>> s set(['a', 'C', 'e', 'w', 'i', 'h', 'o', 'n', 'p', 's', 'r', 'W']) >>> s.remove('Whoispower') Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'Whoispower' >>> s.remove('W') >>> s set(['a', 'C', 'e', 'w', 'i', 'h', 'o', 'n', 'p', 's', 'r']) >>> s-=set('Whoispowerfuu') >>> s set(['a', 'C', 'n']) >>> del t >>> t Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 't' is not defined |
子集与超集:加上等号就是非严格
1 2 3 4 5 6 7 8 9 10 11 12 |
>>> set('china') < set('China') False >>> set('china') <= set('China') False >>> set('china') >= set('China') False >>> set('china') == set('China') False >>> set('china') >= set('china') True >>> set('china') <= set('china') True |
类型操作符:| & – ^
如果同为可变set,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
>>> t = set('china') >>> s = set('beijing') >>> s set(['b', 'e', 'g', 'i', 'j', 'n']) >>> t set(['i', 'h', 'c', 'a', 'n']) >>> s | t set(['a', 'c', 'b', 'e', 'g', 'i', 'h', 'j', 'n']) >>> s & t set(['i', 'n']) >>> s - t set(['b', 'e', 'g', 'j']) >>> s ^ t set(['a', 'c', 'b', 'e', 'g', 'h', 'j']) |
如果不可变,
1 2 3 4 5 6 7 8 |
>>> t = set('china') >>> s = frozenset('beijing') >>> s | t frozenset(['a', 'c', 'b', 'e', 'g', 'i', 'h', 'j', 'n']) >>> s & t frozenset(['i', 'n']) >>> s ^ t frozenset(['a', 'c', 'b', 'e', 'g', 'h', 'j']) |
内建方法:
- s.issubset(t)
- s.issuperset(t)
- s.union(t)
- s.inersection(t)
- s.difference(t)
- s.symmetric_difference(t):返回一个新集合,是s或t的成员,但不包s和t的公共成员;
- s.copy()
仅可变适用于可变集合的方法:
- s.update(t)
- s.intersection_update(t)
- s.difference_update(t)
- s.symmetric_difference_update(t)
- s.add(obj)
- s.remove(obj)
- s.discard(obj)
- s.pop()
- s.clear()
条件和循环
if语句
1 2 |
if 3 > 4: print 'OK' |
多重表达式可以利用and、or、not连接实现
单一语句代码块(不推荐)
else语句
1 2 3 4 |
if 3 > 4: print 'OK' else: print 'Not OK' |
elif语句
1 2 3 4 5 6 |
if 3 > 4: print 'OK' elif 3 > 3.5: print 'KO' else: print 'Not OK' |
条件表达式(三元运算符)
1 2 3 4 5 6 7 8 |
>>> x, y = 3, 4 >>> x 3 >>> y 4 >>> z = x if x > y else y >>> z 4 |
while语句
1 2 |
>>> while x > y: >>> print x, |
文件输入输出
TODO
错误和异常
TODO
函数和函数式编程
TODO
模块
代码量变大时,利用模块组织代码段,可以是包含数据成员的类,也可以是相互独立的操作函数。Python允许引入模块实现重用。
1 模块与文件
模块是按照逻辑组织python代码的方法,文件是物理层上组织模块的方法。文件被认为是独立的模块,模块的文件名就是“模块名.py”,为了避免不同模块间的命名冲突,需要利用全称(Fully qualified name)。
模块导入时的路径搜索:
1 2 3 |
>>> import sys >>> sys.path ['', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload', '/usr/lib64/python2.7/site-packages', '/usr/lib64/python2.7/site-packages/gtk-2.0', '/usr/lib/python2.7/site-packages'] |
修改,加上自定义的模块
1 2 3 |
>>> sys.path.append('/home/user/py/lib') >>> sys.path ['', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload', '/usr/lib64/python2.7/site-packages', '/usr/lib64/python2.7/site-packages/gtk-2.0', '/usr/lib/python2.7/site-packages', '/home/user/py/lib'] |
然后引入自定义的模块即可。
2 名称空间
名称空间是名称(标识符)到对象的映射。
Python解释器首先加载内建空间名称,它由__builtins__模块中名字组成,随后加载执行模块的全局名称空间,它会在模块开始执行后变为活动的名称空间。如果执行期间调用了一个函数,那么将创建第三个名称空间,即局部名称空间,可以通过globals()和locals()内建函数判断某个名字属于那个名称空间。
1 2 3 4 |
>>> globals() {'copy': <module 'copy' from '/usr/lib64/python2.7/copy.pyc'>, '__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'sys': <module 'sys' (built-in)>, 'person': ['joe', ['saving', 59.0]], 't': (['xyz', ['bdc', 'def']], 23, -103.4), 'x': ['saving', 100.0], '__name__': '__main__', 'hubby': ['joe', ['saving', 59.0]], '__doc__': None, 'wifey': ['jane', ['saving', 100.0]]} >>> locals() {'copy': <module 'copy' from '/usr/lib64/python2.7/copy.pyc'>, '__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'sys': <module 'sys' (built-in)>, 'person': ['joe', ['saving', 59.0]], 't': (['xyz', ['bdc', 'def']], 23, -103.4), 'x': ['saving', 100.0], '__name__': '__main__', 'hubby': ['joe', ['saving', 59.0]], '__doc__': None, 'wifey': ['jane', ['saving', 100.0]]} |
3 导入
1 2 |
import module1 import module2 |
导入顺序:Python标准库模块,Python第三方模块,应用程序自定义模块。
指定名称导入到当前作用域
1 |
from module import name1[,name2[,....nameN]] |
扩展import语句as,用来导入时替换原来模块的名称
1 |
from cgi improt FiledStorage as form |
4 导入特性
加载只在第一次导入发生
导入到当前名称空间的名称,使用*会污染名称空间,避免使用
1 |
from module import * |
导入模块名字时,可能会覆盖具有相同的名称对象,解决方法是使用import和完整的标识符名称。
5 模块内建函数
__import__():满足特殊用户需求,可以覆盖import,实现自定义的导入算法,例如
1 2 3 4 5 6 |
>>> import sys >>> sys <module 'sys' (built-in)> >>> sys = __import__('sys') >>> sys <module 'sys' (built-in)> |
globals()和locals():分别返回调用者全局和局部名称空间的字典。在全局名称空间下,两者返回相同字典,例如:
1 2 3 4 5 6 7 8 9 |
def foo(): print '\n calling foo()....' sString = 'bar' anInt = 42 print "foo()'s globals:", globals().keys() print "foo()'s locals:", locals().keys() print "__main__'s globals:", globals().keys() print "__main__'s locals:", locals().keys() foo() |
结果如下:
1 2 3 4 5 6 |
__main__'s globals: ['__builtins__', '__file__', '__package__', '__name__', 'foo', '__doc__'] __main__'s locals: ['__builtins__', '__file__', '__package__', '__name__', 'foo', '__doc__'] calling foo().... foo()'s globals: ['__builtins__', '__file__', '__package__', '__name__', 'foo', '__doc__'] foo()'s locals: ['sString', 'anInt'] |
reload():重新导入一个已存在的模块。语法为reload(module),前提是模块必须是全部导入,必须被成功导入。模块中在import时被执行一次,但reload()不是只执行一次。
6 包
具备层次的文件目录结构,有模块和子包组成。解决以下问题:
- 加入有层次的目录结构
- 运行程序员将关联模块组合在一起
- 运行分发者使用目录结构而不是一堆混乱文件
- 帮助解决有冲突的模块名称
导入:
1 2 3 |
import Phone.Molile.Analog Phone.Mobile.Analog.dial() from Phone.Molile.Analog import dial |
以上为绝对导入,相对导入的例子如下
1 2 3 4 |
from Phone.Molile.Analog import dial from .Analog imort from ..common_util import setup from ..Fax import G3.dial |
7 其他特性
- 自动导入模块:标准模式下启动时,自动导入一些模块,用于系统相关操作;
- 阻止属性导入:不想让模块的属性被”from module import *”导入,可以利用_屏蔽,如import foo._bar
- 源代码编码:默认是ASCII编码,额外编码需要声明,用于制定编码解析源码,例如UTF-8编码,
-
12#!/usr/bin/env python# -*- coding: UTF-8 -*-
-
- 循环导入:解决大型项目中模块循环依赖的问题。
面向对象的编程
__init__():类构造器
类的继承:类声明和构造器中体现,资料可以直接调用父类的方法
类相关的编码风格:类名通常大写字母开头,数据属性应该是数据值的名字,方法应该之处对象或值的行为,使用动词加对象命名,推荐使用驼峰命名+下划线的方式,比如update_phone、set_mail等等。类也叫尽量细致命名,比如AddrBookEntry、RepairShop等。
Python一开始设计就是面向对象的,并且结构上支持OOP,但Python没有限定或强制使用OO代码。
常用术语:抽象实现、封装接口、合成(聚合)、派生/继承/继承结构(多代派生,族谱)、泛化特化、多态、自省/发射。
Python并不支持纯虚函数(C++中)或者抽象方法(Java中),这要求程序员在子类中定义方法,在Python中,可以在基类方法中引发NotImplementedError异常。
类的静态变量
pass关键字作用:一般用做占位语句,当你在编写一个程序时,执行语句部分思路还没有完成,这时你可以用pass语句来占位,也可以当做是一个标记,是要过后来完成的代码。比如下面这样:
1 2 |
>>>def iplaypython(): >>> pass |
定义一个函数iplaypython,但函数体部分暂时还没有完成,又不能空着不写内容,因此可以用pass来替代占个位置。
pass语句在循环中的作用
pass也常用于为复合语句编写一个空的主体,比如说你想一个while语句的无限循环,每次迭代时不需要任何操作,你可以这样写:
1 2 |
>>>while True: >>> pass |
查看类的属性可以可用dir()内建函数,或者使用访问类的字典函数__dict__。
实例属性:python不仅是动态类型,而且是在运行时,允许这些对象属性的动态创建。但使用要谨慎,避免因在条件语句中创建实例属性,但条件不满足时,属性不存在,访问会出错。
组合:让不同的类混合并加入到其他类中。可以在大类中创建自己的类实例。
正则表达式
TODO
网络编程
1 套接字:通信端点
Python支持AF_UNIX、AF_NETLINK和AF_INET。
套接字地址:主机与端口
端口号:0~65535,0~1024系统预留端口。
面向连接的TCP:套接字类型为SOCK_STREAM
无连接的UDP:套接字类型为SOCK_DGRAM(datagram数据报)
2 Socket()模块函数
Python中的socket()模块,套接字对象的内建方法
- 面向服务器端的
- socket.bind():绑定地址到套接字
- socket.listen():开始TCP监听
- socket.accept():被动接受TCP客户端连接,阻塞式等待连接的到来
- 面向客户端的
- socket.connect():主动初始化TCP服务连接
- socket.connect_ex():connect()函数的扩展版本,出错时返回错误码,并非抛出异常
- 公共用途的
- socket.recv():接受TCP数据
- socket.send():发送TCP数据
- socket.sendall():完整发送TCP数据
- socket.recvfrom():接受UDP数据
- socket.sendto():发送UDP数据
- socket.getpeername():连接到当前套接字的远程地址(TCP连接)
- socket.getsockname():当前套接字的地址
- socket.getsockopt():返回指定套接字参数
- socket.setsockopt():设置指定套接字参数
- socket.close():关闭套接字
- 面向模块的
- socket.setblocking():设置套接字的阻塞和非阻塞模式
- socket.settimeout():设置阻塞套接字的超时时间
- socket.gettimeout():获取阻塞套接字的超时时间
- 面向文件的
- socket.fileno():套接字的文字描述符
- socket.makefile():创建一个与该套接字关联的文件对象
TCP范例:
服务器端Socket:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#!/usr/bin/env python from socket import * from time import ctime # # socket programming in server HOST = '' PORT = 21567 BUFSIZE = 1024 ADDR = (HOST, PORT) tcpSerSocket = socket(AF_INET, SOCK_STREAM) tcpSerSocket.bind(ADDR) tcpSerSocket.listen(5) while True: print 'wating for connection...' tcpCliSocket, addr=tcpSerSocket.accept() print '...connected from:', addr while True: data=tcpCliSocket.recv(BUFSIZE) if not data: break tcpCliSocket.send('[%s] %s' % (ctime(), data)) tcpCliSocket.close() tcpSerSocket.close |
客户端Socket:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#!/usr/bin/evnv python # # socket programming in client from socket import * HOST = 'localhost' PORT = 21567 BUFSIZE = 1024 ADDR = (HOST, PORT) tcpCliSock = socket(AF_INET, SOCK_STREAM) tcpCliSock.connect(ADDR) while True: data = raw_input('> ') if not data: break tcpCliSock.send(data) data = tcpCliSock.recv(BUFSIZE) if not data: break print data tcpCliSock.close() |
UDP范例
服务器端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#!/usr/bin/evnv python # # UDP: socket programming in client from socket import * HOST = 'localhost' PORT = 21567 BUFSIZE = 1024 ADDR = (HOST, PORT) udpCliSock = socket(AF_INET, SOCK_DGRAM) while True: data = raw_input('> ') if not data: break udpCliSock.sendto(data, ADDR) data = udpCliSock.recvfrom(BUFSIZE) if not data: break print data udpCliSock.close() |
客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/usr/bin/env python from socket import * from time import ctime # # UDP: socket programming in server HOST = '' PORT = 21567 BUFSIZE = 1024 ADDR = (HOST, PORT) udpSerSocket = socket(AF_INET, SOCK_DGRAM) udpSerSocket.bind(ADDR) while True: print 'wating for message...' data, addr=udpSerSocket.recvfrom(BUFSIZE) udpSerSocket.sendto('[%s] %s' % (ctime(), data), addr) print '...received from and returned to:', addr udpSerSocket.close |
3 SocketServer模块
标准库中高级别模块,为了简化实现网络客户端和服务器的样板代码,模块中可供实现的类
- BaseServer: 包含服务器的核心功能,与max-in类挂钩,此类只能派生,不能生成该类示例,考虑使用TCPServer或UDPServer;
- TCPServer/UDPServer: 基本的网络同步TCP/UDP服务器;
- UnitxStreamServer/UnixDatagramServer: 基本的基于文件同步的TCP/UDP服务器;
- ForkingMixIn/ThreadingMixIn: 实现核心的进程化或线程化的功能,作为混合类,与服务器类一并使用,以提供一些异步特性,不会被实例化
- ForkingTCPServing/ForkingMixIn:
- ThreadingTCPServer/ThreadUDPServer
- BaseRequestHandler: 包含处理服务器请求的核心功能,该类只能派生,考虑使用StreamRequestHandler/DatagramRequestHandler
- StreamRequestHandler/DatagramRequestHandler: 用于TCP/UDP服务器的服务处理工具。
SocketServer范例
服务器端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#!/usr/bin/env python # # SockerServer Moudle: Server from SocketServer import (TCPServer as TCP, StreamRequestHandler as SRH) from time import ctime HOST = '' PORT = 21567 ADDR = (HOST, PORT) class MyRequestHandler(SRH): def handle(self): print '...connected from:', self.client_address self.wfile.write('[%s] %s' % (ctime(), self.rfile.readline())) tcpServ = TCP(ADDR, MyRequestHandler) print 'wating for connection...' tcpServ.serve_forever() |
客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#!/usr/bin/env python # # SockerServer Moudle: Client from socket import * HOST = 'localhost' PORT = 21567 BUFSIZE = 1024 ADDR = (HOST, PORT) while True: tcpCliSock = socket(AF_INET, SOCK_STREAM) tcpCliSock.connect(ADDR) data = tcpCliSock.recv(BUFSIZE) if not data: break tcpCliSock.send('%s\r\n' % data) data = tcpCliSock.recv(BUFSIZE) if not data: break print data.strip() tcpCliSock.close() |
备注:服务器端执行失败!
网络客户端编程
1 FTP
ftplib.FTP类方法:
- login(user=’anonymous’,passwd=”, acct=”) :登录到 FTP 服务器,所有的参数都是可选的
- pwd():得到当前工作目录
- cwd(path): 把当前工作目录设置为 path
- dir([path[,…[,cb]]) :显示 path 目录里的内容,可选的参数 cb 是一个回调函数,它会被传给 retrlines()方法
- nlst([path[,…]) :与 dir()类似,但返回一个文件名的列表,而不是显示这些文件名
- retrlines(cmd [, cb]) :给定 FTP 命令(如“RETR filename”),用于下载文本文件。可选的回调函数 cb 用于处理文件的每一行
- retrbinary(cmd, cb[, bs=8192[, ra]]) :与 retrlines()类似,只是这个指令处理二进制文件。回调函数 cb 用于处理每一块(块大小默认为 8K)下载的数据。
- storlines(cmd, f) :给定 FTP 命令(如“STOR filename”),以上传文本文件。要给定一个文件对象 f
- storbinary(cmd, f[, bs=8192]):与 storlines()类似,只是这个指令处理二进制文件。要给定一个文件对象 f,上传块大小 bs 默认为 8Kbs=8192])
- rename(old, new) 把远程文件 old 改名为 new
- delete(path) : 删除位于 path 的远程文件
- mkd(directory) :创建远程目录
- rmd(directory) :删除远程目录
- quit():关闭连接并退出
交互式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
>>> from ftplib import FTP >>> f=FTP('192.168.100.81') >>> f.login('anonymous','zj@qq.com') '230 Login successful.' >>> f.dir() drwxr-xr-x 11 0 0 4096 Jul 14 12:33 pub >>> f.pwd() '/' >>> f.dir('pub') drwxr-xr-x 5 0 0 82 Sep 27 2016 CentOS-7.2-X86_64 drwxr-xr-x 3 0 0 57 Sep 27 2016 Openstack-Mitaka drwxr-xr-x 3 0 0 24 Sep 28 2016 dl.fedoraproject.org drwxr-xr-x 6 0 0 119 Nov 14 2016 download.ceph.com drwxr-xr-x 5 0 0 4096 May 31 04:53 image drwxrwxrwx 2 0 0 6 Jul 14 12:33 tmp drwxrwxrwx 3 99 99 4096 Jul 05 08:50 tools drwxr-xr-x 2 0 0 132 Sep 29 2016 wheel_ceph drwxr-xr-x 3 0 0 25 Sep 28 2016 yum.mariadb.org >>> f.dir('pub/tools') -rwxrwxrwx 1 99 99 0 Jun 19 09:07 0 -rwxrwxrwx 1 99 99 70087104 Mar 27 04:35 NDP451-KB2858728-x86-x64.3505182529.exe -rwxrwxrwx 1 99 99 123 Jun 01 03:30 allow_ssh.sh -rwxrwxrwx 1 99 99 8537063 Mar 27 02:17 apache-tomcat-6.0.48-windows-x64.zip -rwxrwxrwx 1 99 99 8900822 Apr 24 04:43 apache-tomcat-7.0.68.tar.gz >>> f.pwd() '/' >>> f.nlst() ['pub'] >>> f.nlst('pub/') ['pub/CentOS-7.2-X86_64', 'pub/Openstack-Mitaka', 'pub/dl.fedoraproject.org', 'pub/download.ceph.com', 'pub/image', 'pub/tmp', 'pub/tools', 'pub/wheel_ceph', 'pub/yum.mariadb.org'] >>> f.retrbinary('pub/tools/0') >>> f.quit() '221 Goodbye.' |
客户端FTP程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
#!/usr/bin/env python # # simple ftp client import ftplib import os import socket HOST = '192.168.100.81' DIRN = 'pub/tools/' FILE = 'apache-tomcat-7.0.68.tar.gz' def main(): try: f=ftplib.FTP(HOST) except (socket.error, socket.gaierror), e: print 'ERROR: cannot reach "%s"' % HOST return print '*** Conneted to host "%s"' % HOST try: f.login() except ftplib.error_perm: print 'ERROR: cannot login anonymously' f.quit() return print '*** Logged in as "anonymous"' try: f.cwd(DIRN) except ftplib.error_perm: print 'ERROR: cannot cd to "%s"' % DIRN f.quit() return print '*** Changed to "%s" folder' % DIRN try: f.retrbinary('RETR %s' % FILE,open(FILE,'wb').write) except ftplib.error_perm: print 'ERROR: cannot read file "%s"' % FILE os.unlink(FILE) else: print '*** Downloaded "%s" to CWD' % FILE f.quit() return if __name__ == '__main__': main() |
2 电子邮件
poplib.POP3类方法:
- user(username):发送用户命令,响应应该指示需要密码
pass_(password):发送密码,响应包括邮件数量和邮箱大小。注意:服务器上的邮箱被锁定,直到调用 quit() - stat():获取邮箱状态。结果是2个整数的元组:(message count, mailbox size)
- list([which]):请求消息列表,结果以 (response, [‘mesg_num octets’, …], octets) 的形式。如果设置了 which,则它是要列出的消息
- retr(which):检索整个消息号 which,并设置其看到的标志。结果为 (response, [‘line’, …], octets) 格式
- dele(which):标记消息号 which 以进行删除。在大多数服务器上,删除直到QUIT才被实际执行(主要例外是Eudora QPOP,它通过在任何断开连接上进行未决删除而故意违反RFC)
- noop():没做什么。可能用作保持活动
- utf8():尝试切换到UTF-8模式。如果成功,返回服务器响应,如果不成功则提升 error_proto。在 RFC 6856 中指定。
- quit():注销:提交更改,解锁邮箱,删除连接。
交互模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
>> from poplib import POP3 >>> p=POP3('pop.radi.ac.cn') >>> p.user('zhangjie@radi.ac.cn') '+OK core mail' >>> p.pass_('NULL') '+OK 6588 message(s) [1157813282 byte(s)]' >>> p.stat() (6588, 1157813282) >>> rsp,msg,siz=p.retr(6588) >>> rsp,siz ('+OK 9299 octets', 9299) >>> for eachLine in msg: ... print eachLine ... Received: from mail.apache.org (unknown [159.226.251.7]) >>> p.quit() |
SMTP和POP3范例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#!/usr/bin/env python # # simple mail client from time import sleep from poplib import POP3 from smtplib import SMTP SMTPSVR = 'smtp.163.com' POP3SVR = 'pop.radi.ac.cn' origHdrs = ['From:vpsalerter@163.com', 'TO:zhangjie@radi.ac.cn', 'Subject:Test msg from python client'] origBody = ['xxx', 'yyy', 'zzz'] origMsg = '\r\n\r\n'.join(['\r\n'.join(origHdrs), '\r\n'.join(origBody)]) def send_mail(): try: handle = SMTP(SMTPSVR) handle.login('vpsalerter@163.com', 'Guess') handle.sendmail('vpsalerter@163.com', 'zhangjie@radi.ac.cn', origMsg) handle.close() return 1 except: return 0 def accpet_mail(): try: p = POP3(POP3SVR) p.user('zhangjie@radi.ac.cn') p.pass_('Guess') ret = p.stat() rsp, msg, siz = p.retr(ret[0]) for eachLine in msg: print eachLine except POP3.error_proto, e: print "Login failed:", e if __name__ == "__main__": send_mail() accpet_mail() |
多线程编程
TODO
Web编程
1 urlparse模块
- urlparse.urlparse():将urlstring解析成6个部分,它从urlstring中取得URL,并返回元组 (scheme, netloc, path, parameters, query, fragment);
- urlparse.urlunparse():从一个元组构建一个url,元组类似urlparse返回的,它接收元组(scheme, netloc, path, parameters, query, fragment)后,会重新组成一个具有正确格式的URL,以便供Python的其他HTML解析模块使用;
- urlparse.urlsplit():主要是分析urlstring,返回一个包含5个字符串项目的元组:协议、位置、路径、查询、片段。allow_fragments为False时,该元组的组后一个项目总是空,不管urlstring有没有片段,省略项目的也是空。urlsplit()和urlparse()差不多。不过它不切分URL的参数。适用于遵循RFC2396的URL,每个路径段都支持参数。这样返回的元组就只有5个元素。
- urlparse.urljoin(): urljoin主要是拼接URL,它以base作为其基地址,然后与url中的相对地址相结合组成一个绝对URL地址。函数urljoin在通过为URL基地址附加新的文件名的方式来处理同一位置处的若干文件的时候格外有用。需要注意的是,如果基地址并非以字符/结尾的话,那么URL基地址最右边部分就会被这个相对路径所替换。如果希望在该路径中保留末端目录,应确保URL基地址以字符/结尾。
1 2 3 4 5 6 7 8 9 10 |
import urlparse url = urlparse.urlparse('https://192.168.100.84:9443/publisher/site/pages/login.jag?requestedPage=/publisher/') print url ////ParseResult(scheme='https', netloc='192.168.100.84:9443', path='/publisher/site/pages/login.jag', params='', query='requestedPage=/publisher/', fragment='') u = urlparse.urlunparse(url) print u ////https://192.168.100.84:9443/publisher/site/pages/login.jag?requestedPage=/publisher/ urlstring = urlparse.urljoin('https://192.168.100.84:9443/publisher/','site/pages/login.jag?requestedPage=/publisher/') print urlstring ////https://192.168.100.84:9443/publisher/site/pages/login.jag?requestedPage=/publisher/ |
2 urllib模块
urllib模块提供了一个高级的Web交流库,支持Web协议、HTTP、FTP等等,同时也支持本地文件访问。从而可以实现数据下载。可以避免使用httplib、ftplib等低层模块。
- urllib.urlopen():打开一个给定URL字符串与web连接,并返回文件类的对象,urlopen(urlstr,postQueryData=None),第二个参数是否为请求类型(GET或POST);
- urllib.urlretrieve():可以帮助完成下载文档的处理
- urllib.quote():获取URL数据,并将其编码,quote(urldata,safe=’/’)
- urllib.quote_plus():类似quote(),它还可以将空格编码成(+)号。
- urllib.unquote():与quote()功能相反。
- urllib.unquote_plus()
- urllib.urlencode()
3 urllib2模块
urllib2可以处理更复杂URL打开问题,例如登录验证。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#!/usr/bin/env python # # demo to use urlopen import urllib2 LOGIN = 'nagiosadmin' PASSWD = '123456' URL = 'http://192.168.100.81/nagios/' def handler_version(url): from urlparse import urlparse as up hdlr = urllib2.HTTPBasicAuthHandler() hdlr.add_password('Archives', up(url)[1], LOGIN, PASSWD) opener = urllib2.build_opener(hdlr) urllib2.install_opener(opener) return url def request_version(url): from base64 import encodestring req = urllib2.Request(url) b64str = encodestring('%s:%s' % (LOGIN, PASSWD))[:-1] req.add_header('Authorization', 'Basic %s' % b64str) return req for funcType in ('request', 'handler'): print '*** Using %s:' % funcType.upper() url = eval('%s_version' % funcType)(URL) f = urllib2.urlopen(url).read() print f |
备注:handler_version方法会执行失败。
4 CGI
Python自带的Web服务器: CGIHTTPServer
1 |
# python -m CGIHTTPServer 9090 |
默认端口8000
简单页面示例
friends.htm
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<HTML> <HEAD> <TITLE>CGI Demo(static screen)</TITLE> </HEAD> <BODY><H3>Friends list for: <I>NEW USER</I></H3> <FORM ACTION='cgi-bin/friendA.py'> <B>Enter your Name:</B> <INPUT TYPE='text' NAME=person VALUE='NEW USER' SIZE=15> <P><B>How many friends do you have?</B></P> <INPUT TYPE='radio' NAME=howmany VALUE='0' CHECKED> 0 <INPUT TYPE='radio' NAME=howmany VALUE='10'> 10 <INPUT TYPE='radio' NAME=howmany VALUE='25'> 25 <INPUT TYPE='radio' NAME=howmany VALUE='50'> 50 <INPUT TYPE='radio' NAME=howmany VALUE='100'> 100 <P><INPUT TYPE=submit></P> </FORM> </BODY> </HTML> |
后端处理页面:在向 CGI 脚本返回结果时,须先返回一个适当的 HTTP 头文件再返回 HTML 结果页面。另外,为了区分这些头文件和 HTML 结果页面,需要在两者之间插入一个空行(两个换行符)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# vim cgi-bin/friendA.py #!/usr/bin/env python import cgi reshtml = '''Content-Type: text/html\n <HTML><HEAD><TITLE> Friends CGI Demo (dynamic screen) </TITLE></HEAD> <BODY><H3>Friends list for: <I>%s</I></H3> Your name is: <B>%s</B><P> You have <B>%s</B> friends. </BODY></HTML>''' form = cgi.FieldStorage() who = form['person'].value howmany = form['howmany'].value print reshtml % (who, who, howmany) # chmod +x cgi-bin/friendA.py |
浏览器访问:http://192.168.100.81:9090/friends.htm,然后可以得到提交的结果。
合并版本:页面friendsB.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# cat cgi-bin/friendB.py #!/usr/bin/env python ''' $Id: friends2.py,v 1.1 2000/12/31 01:32:45 wesc Exp $ CGI demo ''' import cgi header = 'Content-Type: text/html\n\n' formhtml = '''<HTML><HEAD><TITLE>Friends CGI Demo</TITLE></HEAD> <BODY><H3>Friends list for: <I>NEW USER</I></H3> <FORM ACTION="friendB.py"> <B>Enter your Name:</B> <INPUT TYPE=hidden NAME=action VALUE=edit> <INPUT TYPE=text NAME=person VALUE="" SIZE=15> <P><B>How many friends do you have?</B> %s <P><INPUT TYPE=submit></FORM></BODY></HTML>''' friendradio = '<INPUT TYPE=radio NAME=howmany VALUE="%s" %s> %s\n' def showForm(): friends = '' for i in [0, 10, 25, 50, 100]: checked = '' if i == 0: checked = 'CHECKED' friends = friends + friendradio % (str(i), checked, str(i)) print header + formhtml % (friends) reshtml = '''<HTML><HEAD><TITLE>Friends CGI Demo</TITLE></HEAD> <BODY><H3>Friends list for: <I>%s</I></H3> Your name is: <B>%s</B><P> You have <B>%s</B> friends. </BODY></HTML>''' def doResults(who, howmany): # substitute in real name and number of friends and return print header + reshtml % (who, who, howmany) # process() does all the work def process(): # initialize Data class object form = cgi.FieldStorage() # get user name if form.has_key('person'): who = form['person'].value else: who = 'NEW USER' # get name and number of friends if form.has_key('howmany'): howmany = form['howmany'].value else: howmany = 0 # if editing, show results if form.has_key('action'): doResults(who, howmany) # otherwise, show form else: showForm() # invoke if called directly if __name__ == '__main__': process() |
浏览器访问:http://192.168.100.81:9090/cgi-bin/friendB.py
5 高级Web客户端
简单的网络爬虫
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
#!/usr/bin/env python # # a web client demo: crawl import cStringIO import formatter from htmllib import HTMLParser import httplib import os import sys import urllib import urlparse class Retriever(object): __slots__ = ('url', 'file') def __init__(self, url): self.url, self.file = self.get_file(url) def get_file(self, url, default='index.html'): 'Create Usable local filename from URL' parsed = urlparse.urlparse(url) host = parsed.netloc.split('@')[-1].split(':')[0] filepath = '%s%s' % (host, parsed.path) if not os.path.splitext(parsed.path)[1]: filepath = os.path.join(filepath,default) linkdir = os.path.dirname(filepath) if not os.path.isdir(linkdir): if os.path.exists(linkdir): os.unlink(linkdir) os.makedirs(linkdir) return url, filepath def download(self): 'Download URL to specific name file' try: retval = urllib.urlretrieve(self.url, self.file) except (IOError, httplib.InvalidURL) as e: retval = (('*** ERROR: bad URL "%s": %s' %( self.url,e)),) return retval def parse_links(self): 'Parse out the links found in downloaded HTML file' f = open(self.file, 'r') data = f.read() f.close() parser = HTMLParser(formatter.AbstractFormatter( formatter.DumbWriter(cStringIO.StringIO()))) parser.feed(data) parser.close() return parser.anchorlist class Crawler(object): count = 0 def __init__(self, url): self.q = [url] self.seen = set() parsed = urlparse.urlparse(url) host = parsed.netloc.split('@')[-1].split(':')[0] self.dom = '.'.join(host.split('.')[-2:]) def get_page(self, url, media=False): 'Download page & parse links, add to queue if nec' r = Retriever(url) fname = r.download()[0] if fname[0] == '*': print fname, '...skipping parse' return Crawler.count += 1; print '\n(', Crawler.count, ')' print 'URL:', url print 'FILE:', fname self.seen.add(url) ftype = os.path.splitext(fname)[1] if ftype not in ('.htm', '.html'): return for link in r.parse_links(): if link.startswith('mailto:'): print '... discard, mailto link' continue if not media: ftype = os.path.splitext(link)[1] if ftype in ('.mp3', '.mp4', '.m4v', '.wav'): print '...discarded, media file' continue if not link.startswith('http://'): link = urlparse.urljoin(url, link) print '*', link, if link not in self.seen: if self.dom not in link: print '...discard, not in domain' else: if link not in self.q: self.q.append(link) print '... new, added to Q ' else: print '... discarded, already in Q' else: print '... discarded, already processed' def go(self, media=False): 'Process next page in queue (if any)' while self.q: url = self.q.pop() self.get_page(url, media) def main(): if len(sys.argv) > 1: url = sys.argv[1] else: try: url = raw_input('Enter starting URL: ') except (KeyboardInterrupt, EOFError): url = '' if not url: return if not url.startswith('http://') and \ not url.startswith('ftp://'): url = 'http://%s/' % url robot = Crawler(url) robot.go() if __name__ == '__main__': main() |
数据库编程
TODO
参考
内容及源码主要来自《Python核心编程·第二版》
code
more code
~~~~