推荐学习书目
推荐网站
推荐IDE
其它资料
好用的第三方库
requests - HTTP库
Flask - RestfulAPI
peewee - 读写数据库,替代SQLAlchemy
aiohttp - 替代requests
BeautifulSoup - HTML解析
scipy - 科学计算
pandas - 处理csv及excel
fdfs_client-py - FastDFS客户端
代码风格规范 参考文档:Google开源项目风格指南 Python代码规范小结
分号 不要在行尾加分号, 也不要用分号将两条命令放在同一行.
行长度 每行不超过80个字符 例外:
长的导入模块语句
注释里的URL
括号 宁缺毋滥的使用括号
1 2 3 4 5 6 7 8 9 10 Yes: if foo: bar() while x: x = bar() if x and y: bar() if not x: bar() return foo for (x, y) in dict.items(): ...
1 2 3 4 5 No: if (x): bar() if not(x): bar() return (foo)
缩进 用4个空格来缩进代码,绝对不要用tab, 也不要tab和空格混用
空行 顶级定义之间空两行, 方法定义之间空一行
空格 按照标准的排版规范来使用标点两边的空格 括号内不要有空格
1 2 Yes: spam(ham[1], {eggs: 2}, [])
1 2 No: spam( ham[ 1 ], { eggs: 2 }, [ ] )
不要在逗号, 分号, 冒号前面加空格, 但应该在它们后面加(除了在行尾).
1 2 3 Yes: if x == 4: print x, y x, y = y, x
1 2 3 No: if x == 4 : print x , y x , y = y , x
参数列表, 索引或切片的左括号前不应加空格.
1 Yes: dict['key'] = list[index]
1 No: dict ['key'] = list [index]
在二元操作符两边都加上一个空格, 比如赋值(=), 比较(==, <, >, !=, <>, <=, >=, in, not in, is, is not), 布尔(and, or, not). 至于算术操作符两边的空格该如何使用, 需要你自己好好判断. 不过两侧务必要保持一致.
当’=’用于指示关键字参数或默认参数值时, 不要在其两侧使用空格.
1 2 Yes: def complex(real, imag=0.0): return magic(r=real, i=imag)
1 No: def complex(real, imag = 0.0): return magic(r = real, i = imag)
不要用空格来垂直对齐多行间的标记, 因为这会成为维护的负担(适用于:, #, =等):
1 2 3 4 5 6 7 8 Yes: foo = 1000 # comment long_name = 2 # comment that should not be aligned dictionary = { "foo": 1, "long_name": 2, }
1 2 3 4 5 6 7 8 No: foo = 1000 # comment long_name = 2 # comment that should not be aligned dictionary = { "foo" : 1, "long_name": 2, }
Shebang 大部分.py文件不必以#!作为文件的开始. 根据 PEP-394 , 程序的main文件应该以 #!/usr/bin/python2或者 #!/usr/bin/python3开始.
注释 文档字符串 Python有一种独一无二的的注释方式: 使用文档字符串. 文档字符串是包, 模块, 类或函数里的第一个语句. 这些字符串可以通过对象的__doc__成员被自动提取, 并且被pydoc所用. 一个文档字符串应该这样组织: 首先是一行以句号, 问号或惊叹号结尾的概述(或者该文档字符串单纯只有一行). 接着是一个空行. 接着是文档字符串剩下的部分, 它应该与文档字符串的第一行的第一个引号对齐.
模块 每个文件应该包含一个许可样板. 根据项目使用的许可(例如, Apache 2.0, BSD, LGPL, GPL), 选择合适的样板.
函数和方法 关于函数的几个方面应该在特定的小节中进行描述记录, 这几个方面如下文所述. 每节应该以一个标题行开始. 标题行以冒号结尾. 除标题行外, 节的其他内容应被缩进2个空格. Args: 列出每个参数的名字, 并在名字后使用一个冒号和一个空格, 分隔对该参数的描述.如果描述太长超过了单行80字符,使用2或者4个空格的悬挂缩进(与文件其他部分保持一致). 描述应该包括所需的类型和含义. 如果一个函数接受foo(可变长度参数列表)或者**bar (任意关键字参数), 应该详细列出 foo和**bar. Returns: (或者 Yields: 用于生成器) 描述返回值的类型和语义. 如果函数返回None, 这一部分可以省略. Raises: 列出与接口有关的所有异常.
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 def fetch_bigtable_rows(big_table, keys, other_silly_variable=None): """Fetches rows from a Bigtable. Retrieves rows pertaining to the given keys from the Table instance represented by big_table. Silly things may happen if other_silly_variable is not None. Args: big_table: An open Bigtable Table instance. keys: A sequence of strings representing the key of each table row to fetch. other_silly_variable: Another optional variable, that has a much longer name than the other args, and which does nothing. Returns: A dict mapping keys to the corresponding table row data fetched. Each row is represented as a tuple of strings. For example: {'Serak': ('Rigel VII', 'Preparer'), 'Zim': ('Irk', 'Invader'), 'Lrrr': ('Omicron Persei 8', 'Emperor')} If a key from the keys argument is missing from the dictionary, then that row was not found in the table. Raises: IOError: An error occurred accessing the bigtable.Table object. """ pass
类 类应该在其定义下有一个用于描述该类的文档字符串. 如果你的类有公共属性(Attributes), 那么文档中应该有一个属性(Attributes)段. 并且应该遵守和函数参数相同的格式.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class SampleClass(object): """Summary of class here. Longer class information.... Longer class information.... Attributes: likes_spam: A boolean indicating if we like SPAM or not. eggs: An integer count of the eggs we have laid. """ def __init__(self, likes_spam=False): """Inits SampleClass with blah.""" self.likes_spam = likes_spam self.eggs = 0 def public_method(self): """Performs operation blah."""
块注释和行注释 1 2 3 4 5 6 # We use a weighted dictionary search to find out where i is in # the array. We extrapolate position based on the largest num # in the array and the array size and then do binary search to # get the exact number. if i & (i-1) == 0: # true iff i is a power of 2
类 如果一个类不继承自其它类, 就显式的从object继承. 嵌套类也一样.
1 2 3 4 5 6 7 8 9 10 11 12 Yes: class SampleClass(object): pass class OuterClass(object): class InnerClass(object): pass class ChildClass(ParentClass): """Explicitly inherits from another class already."""
1 2 3 4 5 6 7 8 No: class SampleClass: pass class OuterClass: class InnerClass: pass
字符串 即使参数都是字符串, 使用%操作符或者格式化方法格式化字符串. 不过也不能一概而论, 你需要在+和%之间好好判定.
1 2 3 4 5 Yes: x = a + b x = '%s, %s!' % (imperative, expletive) x = '{}, {}!'.format(imperative, expletive) x = 'name: %s; score: %d' % (name, n) x = 'name: {}; score: {}'.format(name, n)
1 2 3 4 No: x = '%s%s' % (a, b) # use + in this case x = '{}{}'.format(a, b) # use + in this case x = imperative + ', ' + expletive + '!' x = 'name: ' + name + '; score: ' + str(n)
避免在循环中用+和+=操作符来累加字符串. 由于字符串是不可变的, 这样做会创建不必要的临时对象, 并且导致二次方而不是线性的运行时间. 作为替代方案, 你可以将每个子串加入列表, 然后在循环结束后用 .join 连接列表. (也可以将每个子串写入一个 cStringIO.StringIO 缓存中.)
1 2 3 4 5 Yes: items = ['<table>'] for last_name, first_name in employee_list: items.append('<tr><td>%s, %s</td></tr>' % (last_name, first_name)) items.append('</table>') employee_table = ''.join(items)
1 2 3 4 No: employee_table = '<table>' for last_name, first_name in employee_list: employee_table += '<tr><td>%s, %s</td></tr>' % (last_name, first_name) employee_table += '</table>'
在同一个文件中, 保持使用字符串引号的一致性. 使用单引号’或者双引号”之一用以引用字符串, 并在同一文件中沿用. 在字符串内可以使用另外一种引号, 以避免在字符串中使用.
1 2 3 4 Yes: Python('Why are you hiding your eyes?') Gollum("I'm scared of lint errors.") Narrator('"Good!" thought a happy Python reviewer.')
1 2 3 4 No: Python("Why are you hiding your eyes?") Gollum('The lint. It burns. It burns us.') Gollum("Always the great lint. Watching. Watching.")
为多行字符串使用三重双引号”“”而非三重单引号’‘’. 当且仅当项目中使用单引号’来引用字符串时, 才可能会使用三重’‘’为非文档字符串的多行字符串来标识引用. 文档字符串必须使用三重双引号”“”. 不过要注意, 通常用隐式行连接更清晰, 因为多行字符串与程序其他部分的缩进方式不一致.
1 2 3 Yes: print ("This is much nicer.\n" "Do it this way.\n")
1 2 3 4 No: print """This is pretty ugly. Don't do this. """
文件和sockets 在文件和sockets结束时, 显式的关闭它. 推荐使用 “with”语句 以管理文件:
1 2 3 4 with open("hello.txt") as hello_file: for line in hello_file: print line
对于不支持使用”with”语句的类似文件的对象,使用 contextlib.closing():
1 2 3 4 5 import contextlib with contextlib.closing(urllib.urlopen("http://www.python.org/")) as front_page: for line in front_page: print line
TODO注释 1 2 # TODO(kl@gmail.com): Use a "*" here for string repetition. # TODO(Zeke) Change this to use relations.
导入格式 每个导入应该独占一行
1 2 Yes: import os import sys
每种分组中, 应该根据每个模块的完整包路径按字典序排序, 忽略大小写.
1 2 3 4 5 import foo from foo import bar from foo.bar import baz from foo.bar import Quux from Foob import ar
语句 通常每个语句应该独占一行 不过, 如果测试结果与测试语句在一行放得下, 你也可以将它们放在同一行. 如果是if语句, 只有在没有else时才能这样做. 特别地, 绝不要对 try/except 这样做, 因为try和except不能放在同一行.
1 2 3 4 5 6 7 8 9 10 11 No: if foo: bar(foo) else: baz(foo) try: bar(foo) except ValueError: baz(foo) try: bar(foo) except ValueError: baz(foo)
访问控制 在Python中, 对于琐碎又不太重要的访问函数, 你应该直接使用公有变量来取代它们, 这样可以避免额外的函数调用开销. 当添加更多功能时, 你可以用属性(property)来保持语法的一致性. 另一方面, 如果访问更复杂, 或者变量的访问开销很显著, 那么你应该使用像 get_foo() 和 set_foo() 这样的函数调用. 如果之前的代码行为允许通过属性(property)访问 , 那么就不要将新的访问函数与属性绑定. 这样, 任何试图通过老方法访问变量的代码就没法运行, 使用者也就会意识到复杂性发生了变化.
命名 1 2 3 4 5 6 7 8 9 10 module_name package_name ClassName method_name ExceptionName function_name GLOBAL_VAR_NAME instance_var_name function_parameter_name local_var_name
应该避免的名称:
单字符名称, 除了计数器和迭代器.
包/模块名中的连字符(-)
双下划线开头并结尾的名称(Python保留, 例如init )
命名约定:
所谓”内部(Internal)”表示仅模块内可用, 或者, 在类内是保护或私有的.
用单下划线( _ )开头表示模块变量或函数是protected的(使用import * from时不会包含).
用双下划线( __ )开头的实例变量或方法表示类内私有.
将相关的类和顶级函数放在同一个模块里. 不像Java, 没必要限制一个类一个模块.
对类名使用大写字母开头的单词(如CapWords, 即Pascal风格), 但是模块名应该用小写加下划线的方式(如lower_with_under.py). 尽管已经有很多现存的模块使用类似于CapWords.py这样的命名, 但现在已经不鼓励这样做, 因为如果模块名碰巧和类名一致, 这会让人困扰.
Main 即使是一个打算被用作脚本的文件, 也应该是可导入的. 并且简单的导入不应该导致这个脚本的主功能(main functionality)被执行, 这是一种副作用. 主功能应该放在一个main()函数中.
在Python中, pydoc以及单元测试要求模块必须是可导入的. 你的代码应该在执行主程序前总是检查 if name == ‘main ‘ , 这样当模块被导入时主程序就不会被执行.
1 2 3 4 5 6 def main(): ... if __name__ == '__main__': main()
所有的顶级代码在模块导入时都会被执行. 要小心不要去调用函数, 创建对象, 或者执行那些不应该在使用pydoc时执行的操作.
语法备忘 Python解释器
CPython - 官方版本的解释,C开发
IPython - 基于CPython之上的一个交互式解释器
PyPy - 采用JIT技术,对Python代码进行动态编译,提高执行速度
Jython - 运行在Java平台
IronPython - 运行在微软.Net平台
基本输入输出 1 2 print('hello, world') name = input()
数据类型 整数 1,100,-8080,0 十六进制 0xff00,0xa5b4c3d2
浮点数 1.23,3.14,-9.01 浮点数运算有误差
字符串 字符串是以单引号’或双引号”括起来的任意文本,比如’abc’,”xyz” 如果字符串内部既包含’又包含”,可以用转义字符\来标识
\n表示换行,\t表示制表符,字符 \ 本身也要转义
Python允许用’’’…’’’的格式表示多行内容
1 2 3 print('''line1 line2 line3''')
布尔值 True False
空值 None
变量 1 2 3 4 a = 123 # a是整数 print(a) a = 'ABC' # a变为字符串 print(a)
常量 通常用全部大写的变量名表示常量: PI = 3.14159265359 但事实上PI仍然是一个变量,Python根本没有任何机制保证PI不会被改变
除法 有两种除法,一种除法是/,即使是两个整数恰好整除,结果也是浮点数:
1 2 3 4 5 >>> 10 / 3 3.3333333333333335 >>> 9 / 3 3.0
还有一种除法是//,称为地板除,两个整数的除法仍然是整数:
余数运算:
字符串 1 2 3 4 5 6 7 8 >>> ord('A') 65 >>> ord('中') 20013 >>> chr(66) 'B' >>> chr(25991) '文'
1 2 3 4 >>> len('ABC') 3 >>> len('中文') 2
1 2 #!/usr/bin/env python3 # -*- coding: utf-8 -*-
1 2 3 4 >>> 'Hello, %s' % 'world' 'Hello, world' >>> 'Hi, %s, you have $%d.' % ('Michael', 1000000) 'Hi, Michael, you have $1000000.'
%d 整数 %f 浮点数 %s 字符串 %x 十六进制整数
list list是一种有序的集合,可以随时添加和删除其中的元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 >>> classmates = ['Michael', 'Bob', 'Tracy'] >>> classmates ['Michael', 'Bob', 'Tracy'] >>> len(classmates) 3 >>> classmates[0] 'Michael' >>> classmates[1] 'Bob' >>> classmates[2] 'Tracy' >>> classmates[-1] 'Tracy'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 >>> classmates.append('Adam') >>> classmates ['Michael', 'Bob', 'Tracy', 'Adam'] >>> classmates.insert(1, 'Jack') >>> classmates ['Michael', 'Jack', 'Bob', 'Tracy', 'Adam'] >>> classmates.pop() 'Adam' >>> classmates ['Michael', 'Jack', 'Bob', 'Tracy'] >>> classmates.pop(1) 'Jack' >>> classmates ['Michael', 'Bob', 'Tracy'] >>> classmates[1] = 'Sarah' >>> classmates ['Michael', 'Sarah', 'Tracy']
list里面的元素的数据类型也可以不同
1 2 >>> L = ['Apple', 123, True]
list元素也可以是另一个list,比如
1 2 3 >>> s = ['python', 'java', ['asp', 'php'], 'scheme'] >>> len(s) 4
tuple tuple和list非常类似,但是tuple一旦初始化就不能修改
1 2 >>> classmates = ('Michael', 'Bob', 'Tracy')
只有1个元素的tuple定义时必须加一个逗号,,来消除歧义:
条件 1 2 3 4 5 6 7 age = 3 if age >= 18: print('adult') elif age >= 6: print('teenager') else: print('kid')
循环 1 2 3 names = ['Michael', 'Bob', 'Tracy'] for name in names: print(name)
1 2 3 4 5 6 sum = 0 n = 99 while n > 0: sum = sum + n n = n - 2 print(sum)
dict 1 2 3 >>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85} >>> d['Michael'] 95
1 2 3 4 >>> d.pop('Bob') 75 >>> d {'Michael': 95, 'Tracy': 85}
set set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。
1 2 3 >>> s = set([1, 1, 2, 2, 3, 3]) >>> s {1, 2, 3}
1 2 3 4 5 6 >>> s.add(4) >>> s {1, 2, 3, 4} >>> s.add(4) >>> s {1, 2, 3, 4}
1 2 3 >>> s.remove(4) >>> s {1, 2, 3}
内置函数 1 2 3 4 >>> abs(-20) 20 max(2, 3, 1, -5) 3
1 2 3 4 5 6 7 8 9 10 11 12 >>> int(12.34) 12 >>> float('12.34') 12.34 >>> str(1.23) '1.23' >>> str(100) '100' >>> bool(1) True >>> bool('') False
定义函数 1 2 def power(x): return x * x
默认参数
1 2 3 4 5 6 def power(x, n=2): s = 1 while n > 0: n = n - 1 s = s * x return s
可变参数
1 2 3 4 5 6 7 8 9 10 11 12 13 def calc(*numbers): sum = 0 for n in numbers: sum = sum + n * n return sum >>> calc(1, 2) 5 >>> calc() 0 >>> nums = [1, 2, 3] >>> calc(*nums) 14
关键字参数
1 2 3 4 5 6 7 8 9 10 11 def person(name, age, **kw): print('name:', name, 'age:', age, 'other:', 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'} >>> extra = {'city': 'Beijing', 'job': 'Engineer'} >>> person('Jack', 24, **extra) name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
命名关键字参数
1 2 3 4 5 6 7 8 def person(name, age, **kw): if 'city' in kw: # 有city参数 pass if 'job' in kw: # 有job参数 pass print('name:', name, 'age:', age, 'other:', kw)
参数组合
1 2 3 4 5 def f1(a, b, c=0, *args, **kw): print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw) def f2(a, b, c=0, *, d, **kw): print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
递归函数 1 2 3 4 5 6 7 def fact(n): return fact_iter(n, 1) def fact_iter(num, product): if num == 1: return product return fact_iter(num - 1, num * product)
切片 1 2 3 4 5 6 7 8 9 10 11 12 13 14 >>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'] >>> L[0:3] ['Michael', 'Sarah', 'Tracy'] >>> L[:3] ['Michael', 'Sarah', 'Tracy'] >>> L[-2:] ['Bob', 'Jack'] >>> L[-2:-1] ['Bob']
1 2 3 4 5 6 7 8 9 10 11 12 >>> L = list(range(100)) >>> L [0, 1, 2, 3, ..., 99] >>> L[:10:2] # 前10个数,每两个取一个: [0, 2, 4, 6, 8] >>> L[::5] # 所有数,每5个取一个 [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95] >>> L[:] # 原样复制一个list [0, 1, 2, 3, ..., 99]
迭代 1 2 3 4 5 6 7 >>> d = {'a': 1, 'b': 2, 'c': 3} >>> for key in d: ... print(key) ... a c b
1 2 3 4 5 6 7 >>> d = {'x': 'A', 'y': 'B', 'z': 'C' } >>> for k, v in d.items(): ... print(k, '=', v) ... y = B x = A z = C
1 2 3 4 5 6 >>> for ch in 'ABC': ... print(ch) ... A B C
判断一个对象是可迭代对象:
1 2 3 4 5 6 7 >>> from collections import Iterable >>> isinstance('abc', Iterable) # str是否可迭代 True >>> isinstance([1,2,3], Iterable) # list是否可迭代 True >>> isinstance(123, Iterable) # 整数是否可迭代 False
列表生成 1 2 3 >>> list(range(1, 11)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
1 2 >>> [x * x for x in range(1, 11)] [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
1 2 >>> [x * x for x in range(1, 11) if x % 2 == 0] [4, 16, 36, 64, 100]
1 2 3 >>> d = {'x': 'A', 'y': 'B', 'z': 'C' } >>> [k + '=' + v for k, v in d.items()] ['y=B', 'x=A', 'z=C']
1 2 3 >>> L = ['Hello', 'World', 'IBM', 'Apple'] >>> [s.lower() for s in L] ['hello', 'world', 'ibm', 'apple']
生成器 这种一边循环一边计算的机制,称为生成器:generator。 只要把一个列表生成式的[]改成(),就创建了一个generator
1 2 3 4 5 6 >>> L = [x * x for x in range(10)] >>> L [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> g = (x * x for x in range(10)) >>> g <generator object <genexpr> at 0x1022ef630>
定义模块 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #!/usr/bin/env python3 # -*- coding: utf-8 -*- ' a test module ' __author__ = 'Michael Liao' import sys def test(): args = sys.argv if len(args)==1: print('Hello, world!') elif len(args)==2: print('Hello, %s!' % args[1]) else: print('Too many arguments!') if __name__=='__main__': test()
第三方模块
默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中:
1 2 3 >>> import sys >>> sys.path ['', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python34.zip', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages']
添加自己的搜索目录,一是直接修改sys.path,添加要搜索的目录:
1 2 >>> import sys >>> sys.path.append('/Users/michael/my_py_scripts')
第二种方法是设置环境变量PYTHONPATH,该环境变量的内容会被自动添加到模块搜索路径中。
面向对象 1 2 3 4 5 6 7 8 9 10 11 12 class Student(object): def __init__(self, name, score): self.name = name self.score = score >>> bart = Student('Bart Simpson', 59) >>> bart.name 'Bart Simpson' >>> bart.score 59
1 2 3 4 5 6 7 8 9 10 11 class Student(object): def __init__(self, name, score): self.name = name self.score = score def print_score(self): print('%s: %s' % (self.name, self.score)) >>> bart.print_score() Bart Simpson: 59
访问限制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Student(object): def __init__(self, name, score): self.__name = name self.__score = score def print_score(self): print('%s: %s' % (self.__name, self.__score)) def get_name(self): return self.__name def get_score(self): return self.__score def set_score(self, score): self.__score = score
继承和多态 1 2 3 class Animal(object): def run(self): print('Animal is running...')
1 2 3 4 5 6 7 8 9 class Dog(Animal): def run(self): print('Dog is running...') class Cat(Animal): def run(self): print('Cat is running...')
1 2 3 4 5 6 7 >>> isinstance(a, list) True >>> isinstance(b, Animal) True >>> isinstance(c, Dog) True
获取对象信息 1 2 3 4 5 6 7 8 9 10 11 >>> type(123) <class 'int'> >>> type('str') <class 'str'> >>> type(None) <type(None) 'NoneType'> >>> type(abs) <class 'builtin_function_or_method'> >>> type(a) <class '__main__.Animal'>
1 2 3 4 5 6 7 8 9 10 11 12 >>> import types >>> def fn(): ... pass ... >>> type(fn)==types.FunctionType True >>> type(abs)==types.BuiltinFunctionType True >>> type(lambda x: x)==types.LambdaType True >>> type((x for x in range(10)))==types.GeneratorType True
1 2 3 4 5 6 7 8 >>> isinstance(h, Husky) True >>> isinstance('a', str) True >>> isinstance(123, int) True >>> isinstance(b'a', bytes) True
1 2 3 >>> dir('ABC') ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
实例属性和类属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> class Student(object): ... name = 'Student' ... >>> s = Student() # 创建实例s >>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性 Student >>> print(Student.name) # 打印类的name属性 Student >>> s.name = 'Michael' # 给实例绑定name属性 >>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性 Michael >>> print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问 Student >>> del s.name # 如果删除实例的name属性 >>> print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了 Student
错误处理 1 2 3 4 5 6 7 8 9 10 try: print('try...') r = 10 / 0 print('result:', r) except ZeroDivisionError as e: print('except:', e) finally: print('finally...') print('END')
调试 1 2 3 4 5 6 7 def foo(s): n = int(s) assert n != 0, 'n is zero!' return 10 / n def main(): foo('0')
1 2 3 4 5 6 import logging s = '0' n = int(s) logging.info('n = %d' % n) print(10 / n)
1 2 $ python3 -m pdb err.py
文件读写 读文件
1 2 3 4 >>> f = open('/Users/michael/test.txt', 'r') >>> f.read() 'Hello, world!' >>> f.close()
1 2 with open('/path/to/file', 'r') as f: print(f.read())
1 2 for line in f.readlines(): print(line.strip())
二进制文件
1 2 3 >>> f = open('/Users/michael/test.jpg', 'rb') >>> f.read() b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六进制表示的字节
指定字符编码
1 2 3 >>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk') >>> f.read() '测试'
写文件
1 2 3 >>> f = open('/Users/michael/test.txt', 'w') >>> f.write('Hello, world!') >>> f.close()
1 2 with open('/Users/michael/test.txt', 'w') as f: f.write('Hello, world!')
操作文件与目录 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 >>> import os >>> os.name # 操作系统类型 'posix' >>> os.uname() posix.uname_result(sysname='Darwin', nodename='MichaelMacPro.local', release='14.3.0', version='Darwin Kernel Version 14.3.0: Mon Mar 23 11:59:05 PDT 2015; root:xnu-2782.20.48~5/RELEASE_X86_64', machine='x86_64') >>> os.environ environ({'VERSIONER_PYTHON_PREFER_32_BIT': 'no', 'TERM_PROGRAM_VERSION': '326', 'LOGNAME': 'michael', 'USER': 'michael', 'PATH': '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/mysql/bin', ...}) >>> os.environ.get('PATH') '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/usr/local/mysql/bin' >>> os.environ.get('x', 'default') 'default' # 查看当前目录的绝对路径: >>> os.path.abspath('.') '/Users/michael' # 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来: >>> os.path.join('/Users/michael', 'testdir') '/Users/michael/testdir' # 然后创建一个目录: >>> os.mkdir('/Users/michael/testdir') # 删掉一个目录: >>> os.rmdir('/Users/michael/testdir') >>> os.path.split('/Users/michael/testdir/file.txt') ('/Users/michael/testdir', 'file.txt') >>> os.path.splitext('/path/to/file.txt') ('/path/to/file', '.txt') # 对文件重命名: >>> os.rename('test.txt', 'test.py') # 删掉文件: >>> os.remove('test.py')
内建模块datatime 获取当前日期和时间
1 2 3 4 >>> from datetime import datetime >>> now = datetime.now() # 获取当前datetime >>> print(now) 2015-05-18 16:28:07.198690
获取指定日期和时间
1 2 3 4 >>> from datetime import datetime >>> dt = datetime(2015, 4, 19, 12, 20) # 用指定日期时间创建datetime >>> print(dt) 2015-04-19 12:20:00
datetime转换为timestamp
1 2 3 4 >>> from datetime import datetime >>> dt = datetime(2015, 4, 19, 12, 20) # 用指定日期时间创建datetime >>> dt.timestamp() # 把datetime转换为timestamp 1429417200.0
timestamp转换为datetime
1 2 3 4 >>> from datetime import datetime >>> t = 1429417200.0 >>> print(datetime.fromtimestamp(t)) 2015-04-19 12:20:00
str转换为datetime
1 2 3 4 >>> from datetime import datetime >>> cday = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S') >>> print(cday) 2015-06-01 18:19:59
datetime转换为str
1 2 3 4 >>> from datetime import datetime >>> now = datetime.now() >>> print(now.strftime('%a, %b %d %H:%M')) Mon, May 05 16:28
datetime加减
1 2 3 4 5 6 7 8 9 10 >>> from datetime import datetime, timedelta >>> now = datetime.now() >>> now datetime.datetime(2015, 5, 18, 16, 57, 3, 540997) >>> now + timedelta(hours=10) datetime.datetime(2015, 5, 19, 2, 57, 3, 540997) >>> now - timedelta(days=1) datetime.datetime(2015, 5, 17, 16, 57, 3, 540997) >>> now + timedelta(days=2, hours=12) datetime.datetime(2015, 5, 21, 4, 57, 3, 540997)
内建模块base64 Base64是一种用64个字符来表示任意二进制数据的方法。 Base64编码会把3字节的二进制数据编码为4字节的文本数据,长度增加33%。 如果二进制数据不是3的倍数,最后剩下1个或2个字节,Base64用\x00
字节在末尾补足后,再在编码的末尾加上1个或2个=号,表示补了多少字节,解码的时候,会自动去掉。
1 2 3 4 5 >>> import base64 >>> base64.b64encode(b'binary\x00string') b'YmluYXJ5AHN0cmluZw==' >>> base64.b64decode(b'YmluYXJ5AHN0cmluZw==') b'binary\x00string'
内建模块hashlib hashlib提供了常见的摘要算法,如MD5,SHA1等等
1 2 3 4 5 import hashlib md5 = hashlib.md5() md5.update('how to use md5 in python hashlib?'.encode('utf-8')) print(md5.hexdigest()) d26a53750bc40b38b65a520292f69306
内建模块contextlib 使用try...finally
关闭资源
1 2 3 4 5 6 try: f = open('/path/to/file', 'r') f.read() finally: if f: f.close()
使用with
关闭资源,必须实现了上下文管理(通过__enter__和__exit__)
1 2 with open('/path/to/file', 'r') as f: f.read()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Query(object): def __init__(self, name): self.name = name def __enter__(self): print('Begin') return self def __exit__(self, exc_type, exc_value, traceback): if exc_type: print('Error') else: print('End') def query(self): print('Query info about %s...' % self.name) with Query('Bob') as q: q.query()
@contextmanager,用于简化enter__和__exit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from contextlib import contextmanager class Query(object): def __init__(self, name): self.name = name def query(self): print('Query info about %s...' % self.name) @contextmanager def create_query(name): print('Begin') q = Query(name) yield q print('End') with create_query('Bob') as q: q.query()
望在某段代码执行前后自动执行特定代码,也可以用@contextmanager实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @contextmanager def tag(name): print("<%s>" % name) yield print("</%s>" % name) with tag("h1"): print("hello") print("world") <h1> hello world </h1>
@closing,把对象变为上下文对象
1 2 3 4 5 6 from contextlib import closing from urllib.request import urlopen with closing(urlopen('https://www.python.org')) as page: for line in page: print(line)
内建模块urllib 1 2 3 4 5 6 7 from urllib import request with request.urlopen('https://api.douban.com/v2/book/2129650') as f: data = f.read() print('Status:', f.status, f.reason) for k, v in f.getheaders(): print('%s: %s' % (k, v)) print('Data:', data.decode('utf-8'))
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 from urllib import request, parse print('Login to weibo.cn...') email = input('Email: ') passwd = input('Password: ') login_data = parse.urlencode([ ('username', email), ('password', passwd), ('entry', 'mweibo'), ('client_id', ''), ('savestate', '1'), ('ec', ''), ('pagerefer', 'https://passport.weibo.cn/signin/welcome?entry=mweibo&r=http%3A%2F%2Fm.weibo.cn%2F') ]) req = request.Request('https://passport.weibo.cn/sso/login') req.add_header('Origin', 'https://passport.weibo.cn') req.add_header('User-Agent', 'Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25') req.add_header('Referer', 'https://passport.weibo.cn/signin/login?entry=mweibo&res=wel&wm=3349&r=http%3A%2F%2Fm.weibo.cn%2F') with request.urlopen(req, data=login_data.encode('utf-8')) as f: print('Status:', f.status, f.reason) for k, v in f.getheaders(): print('%s: %s' % (k, v)) print('Data:', f.read().decode('utf-8'))
urllib可以用requests替代
第三方模块PIL 安装
1 sudo pip3 install pillow
图像缩放
1 2 3 4 5 6 7 8 9 10 11 12 from PIL import Image # 打开一个jpg图像文件,注意是当前路径: im = Image.open('test.jpg') # 获得图像尺寸: w, h = im.size print('Original image size: %sx%s' % (w, h)) # 缩放到50%: im.thumbnail((w//2, h//2)) print('Resize image to: %sx%s' % (w//2, h//2)) # 把缩放后的图像用jpeg格式保存: im.save('thumbnail.jpg', 'jpeg')
生成字母验证码
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 PIL import Image, ImageDraw, ImageFont, ImageFilter import random # 随机字母: def rndChar(): return chr(random.randint(65, 90)) # 随机颜色1: def rndColor(): return (random.randint(64, 255), random.randint(64, 255), random.randint(64, 255)) # 随机颜色2: def rndColor2(): return (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127)) # 240 x 60: width = 60 * 4 height = 60 image = Image.new('RGB', (width, height), (255, 255, 255)) # 创建Font对象: font = ImageFont.truetype('Arial.ttf', 36) # 创建Draw对象: draw = ImageDraw.Draw(image) # 填充每个像素: for x in range(width): for y in range(height): draw.point((x, y), fill=rndColor()) # 输出文字: for t in range(4): draw.text((60 * t + 10, 10), rndChar(), font=font, fill=rndColor2()) # 模糊: image = image.filter(ImageFilter.BLUR) image.save('code.jpg', 'jpeg')
图形界面 Python支持多种图形界面的第三方库,包括:
Tk
wxWidgets
Qt
GTK Python自带的库是支持Tk的Tkinter,使用Tkinter,无需安装任何包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from tkinter import * import tkinter.messagebox as messagebox class Application(Frame): def __init__(self, master=None): Frame.__init__(self, master) self.pack() self.createWidgets() def createWidgets(self): self.nameInput = Entry(self) self.nameInput.pack() self.alertButton = Button(self, text='Hello', command=self.hello) self.alertButton.pack() def hello(self): name = self.nameInput.get() or 'world' messagebox.showinfo('Message', 'Hello, %s' % name) app = Application() # 设置窗口标题: app.master.title('Hello World') # 主消息循环: app.mainloop()
发送邮件 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 from email import encoders from email.header import Header from email.mime.text import MIMEText from email.utils import parseaddr, formataddr import smtplib def _format_addr(s): name, addr = parseaddr(s) return formataddr((Header(name, 'utf-8').encode(), addr)) from_addr = input('From: ') password = input('Password: ') to_addr = input('To: ') smtp_server = input('SMTP server: ') msg = MIMEText('hello, send by Python...', 'plain', 'utf-8') msg['From'] = _format_addr('Python爱好者 <%s>' % from_addr) msg['To'] = _format_addr('管理员 <%s>' % to_addr) msg['Subject'] = Header('来自SMTP的问候……', 'utf-8').encode() # 邮件正文是MIMEText: msg.attach(MIMEText('send with file...', 'plain', 'utf-8')) # 添加附件就是加上一个MIMEBase,从本地读取一个图片: with open('/Users/michael/Downloads/test.png', 'rb') as f: # 设置附件的MIME和文件名,这里是png类型: mime = MIMEBase('image', 'png', filename='test.png') # 加上必要的头信息: mime.add_header('Content-Disposition', 'attachment', filename='test.png') mime.add_header('Content-ID', '<0>') mime.add_header('X-Attachment-Id', '0') # 把附件的内容读进来: mime.set_payload(f.read()) # 用Base64编码: encoders.encode_base64(mime) # 添加到MIMEMultipart: msg.attach(mime) server = smtplib.SMTP(smtp_server, 25) server.set_debuglevel(1) server.login(from_addr, password) server.sendmail(from_addr, [to_addr], msg.as_string()) server.quit()
MySQL 1 sudo pip3 install mysql-connector
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # 导入MySQL驱动: >>> import mysql.connector # 注意把password设为你的root口令: >>> conn = mysql.connector.connect(user='root', password='password', database='test') >>> cursor = conn.cursor() # 创建user表: >>> cursor.execute('create table user (id varchar(20) primary key, name varchar(20))') # 插入一行记录,注意MySQL的占位符是%s: >>> cursor.execute('insert into user (id, name) values (%s, %s)', ['1', 'Michael']) >>> cursor.rowcount 1 # 提交事务: >>> conn.commit() >>> cursor.close() # 运行查询: >>> cursor = conn.cursor() >>> cursor.execute('select * from user where id = %s', ('1',)) >>> values = cursor.fetchall() >>> values [('1', 'Michael')] # 关闭Cursor和Connection: >>> cursor.close() True >>> conn.close()
多进程 1 2 3 4 5 6 7 8 9 import os print('Process (%s) start...' % os.getpid()) # Only works on Unix/Linux/Mac: pid = os.fork() if pid == 0: print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())) else: print('I (%s) just created a child process (%s).' % (os.getpid(), pid))
multiprocessing模块就是跨平台版本的多进程模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from multiprocessing import Process import 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('Child process will start.') p.start() p.join() print('Child process end.')
用进程池的方式批量创建子进程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from multiprocessing import Pool import os, time, random def long_time_task(name): print('Run task %s (%s)...' % (name, os.getpid())) start = time.time() time.sleep(random.random() * 3) 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(long_time_task, args=(i,)) print('Waiting for all subprocesses done...') p.close() p.join() print('All subprocesses done.')