python下载
python文档
python异常
python规范
0. 命名规范
Type | Public | Internal |
---|---|---|
Modules模块 | lower_with_under | _lower_with_under |
Packages包 | lower_with_under | |
Classes类 | CapWords | _CapWords |
Exceptions异常 | CapWords | |
Functions函数 | lower_with_under() | _lower_with_under() |
Global/Class Constants全局/类常量 | CAPS_WITH_UNDER | _CAPS_WITH_UNDER |
Global/Class Variables全局/类变量 | lower_with_under | _lower_with_under |
Instance Variables实例变量 | lower_with_under | _lower_with_under (protected) or __lower_with_under (private) |
Method Names方法 | lower_with_under() | _lower_with_under() (protected) or __lower_with_under() (private) |
Function/Method Parameters参数 | lower_with_under | |
Local Variables本地变量 | lower_with_under |
1. 直接运行py文件
在.py
文件的第一行加上:
1 | #!/usr/bin/env python |
再执行1
$ chmod a+x hello.py
然后就可以直接运行./hello.py
2. 按UTF-8编码读取
在文件开头添加1
# -*- coding: utf-8 -*-
3. 占位符
其中,格式化整数和浮点数还可以指定是否补0和整数与小数的位数:1
2
3
4'%2d-%02d' % (3, 1)
' 3-01'
'%.2f' % 3.1415926
'3.14'
如果你不太确定应该用什么,%s永远起作用,它会把任何数据类型转换为字符串:1
2'Age: %s. Gender: %s' % (25, True)
'Age: 25. Gender: True'
对于Unicode字符串,用法完全一样,但最好确保替换的字符串也是Unicode字符串:1
2u'Hi, %s' % u'Michael'
u'Hi, Michael'
有些时候,字符串里面的%是一个普通字符怎么办?这个时候就需要转义,用%%来表示一个%:1
2'growth rate: %d %%' % 7
'growth rate: 7 %'
4. tuple定义
1 | 1) t = ( |
括号()既可以表示tuple,又可以表示数学公式中的小括号,这就产生了歧义,因此,Python规定,这种情况下,按小括号进行计算,计算结果自然是1。
所以,只有1个元素的tuple定义时必须加一个逗号,,来消除歧义:
1 | 1,) t = ( |
Python在显示只有1个元素的tuple时,也会加一个逗号,
,以免你误解成数学计算意义上的括号。
“可变的”tuple:1
2
3
4
5'a', 'b', ['A', 'B']) t = (
2][0] = 'X' t[
2][10] = 'Y' t[
t
('a', 'b', ['X', 'Y'])
表面上看,tuple的元素确实变了,但其实变的不是tuple的元素,而是list的元素。tuple一开始指向的list并没有改成别的list,所以,tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向’a’,就不能改成指向’b’,指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的。
5. pass
如果想定义一个什么事也不做的空函数,可以用pass语句:1
2def nop():
pass
pass语句什么都不做,那有什么用?实际上pass可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来。
pass还可以用在其他语句里,比如:1
2if age >= 18:
pass
缺少了pass,代码运行就会有语法错误。
6. 函数返回值是个tuple
1 | import math |
这样我们就可以同时获得返回值:1
2
3100, 100, 60, math.pi / 6) x, y = move(
print x, y
151.961524227 70.0
但其实这只是一种假象,Python函数返回的仍然是单一值:1
2
3100, 100, 60, math.pi / 6) r = move(
print r
(151.96152422706632, 70.0)
原来返回值是一个tuple!但是,在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便。
7. 关键字参数
可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。请看示例:1
2def person(name, age, **kw):
print 'name:', name, 'age:', age, 'other:', kw
函数person除了必选参数name和age外,还接受关键字参数kw。在调用该函数时,可以只传入必选参数
1 | 'Michael', 30) person( |
也可以传入任意个数的关键字参数:1
2
3
4'Bob', 35, city='Beijing') person(
name: Bob age: 35 other: {'city': 'Beijing'}
'Adam', 45, gender='M', job='Engineer') person(
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
关键字参数有什么用?它可以扩展函数的功能。比如,在person函数里,我们保证能接收到name和age这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。
和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去:1
2
3'city': 'Beijing', 'job': 'Engineer'} kw = {
'Jack', 24, city=kw['city'], job=kw['job']) person(
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
当然,上面复杂的调用可以用简化的写法:
1 | 'city': 'Beijing', 'job': 'Engineer'} kw = { |
8. 尾递归
在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
解决递归调用栈溢出的方法是通过尾递归优化,尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
上面的fact(n)函数由于return n * fact(n - 1)
引入了乘法表达式,所以就不是尾递归了。要改成尾递归方式,需要多一点代码,主要是要把每一步的乘积传入到递归函数中:1
2
3
4
5
6
7def fact(n):
return fact_iter(n, 1)
def fact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num * product)
可以看到,return fact_iter(num - 1, num * product)
仅返回递归函数本身,num - 1
和num * product
在函数调用前就会被计算,不影响函数调用。
fact(5)对应的fact_iter(5, 1)的调用如下:1
2
3
4
5
6===> fact_iter(5, 1)
===> fact_iter(4, 5)
===> fact_iter(3, 20)
===> fact_iter(2, 60)
===> fact_iter(1, 120)
===> 120
尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。
Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。
9. 特性
1. 切片
1 | 100) L = range( |
可以通过切片轻松取出某一段数列。比如前10个数:1
210] L[:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
后10个数:1
2-10:] L[
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
前11-20个数:1
210:20] L[
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
前10个数,每两个取一个:1
210:2] L[:
[0, 2, 4, 6, 8]
2. 迭代
Python的for循环不仅可以用在list或tuple上,还可以作用在其他可迭代对象上。
只要是可迭代对象,无论有无下标,都可以迭代
dict迭代:1
2
3
4
5
6
7'a': 1, 'b': 2, 'c': 3} d = {
for key in d:
print key
...
a
c
b
默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.itervalues()
,如果要同时迭代key和value,可以用for k, v in d.iteritems()
。
由于字符串也是可迭代对象,因此,也可以作用于for循环:1
2
3
4
5
6for ch in 'ABC':
print ch
...
A
B
C
判断一个对象是可迭代对象的方法是通过collections模块的Iterable类型判断:1
2
3
4
5
6
7from collections import Iterable
'abc', Iterable) # str是否可迭代 isinstance(
True
1,2,3], Iterable) # list是否可迭代 isinstance([
True
123, Iterable) # 整数是否可迭代 isinstance(
False
Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:1
2
3
4
5
6for i, value in enumerate(['A', 'B', 'C']):
print i, value
...
0 A
1 B
2 C
上面的for循环里,同时引用了两个变量,在Python里是很常见的,比如下面的代码:1
2
3
4
5
6for x, y in [(1, 1), (2, 4), (3, 9)]:
print x, y
...
1 1
2 4
3 9
3. 列表生成式
要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用range(1, 11):1
21, 11) range(
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
如果要生成[1x1, 2x2, 3x3, …, 10x10]?方法一是循环:1
2
3
4
5
6 L = []
for x in range(1, 11):
L.append(x * x)
...
L
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
列表生成式则可以用一行语句代替循环生成上面的list:1
2for x in range(1, 11)] [x * x
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
还可以使用两层循环,可以生成全排列:1
2for m in 'ABC' for n in 'XYZ'] [m + n
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
列出当前目录下的所有文件和目录名
1 | import os # 导入os模块,模块的概念后面讲到 |
4. 生成器
一边循环一边计算的机制,称为生成器(Generator)
可以从第一个元素开始,推算出后续任意的元素
只要把一个列表生成式的[]改成(),就创建了一个generator:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15for x in range(10)] #list L = [x * x
L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
for x in range(10)) g = (x * x
g
<generator object <genexpr> at 0x104feab40>
g.next()
0
g.next()
1
g.next()
4
for x in range(10)) g = (x * x
for n in g:
print n
斐波那契数列
1 | def fib(max): |
要把fib函数变成generator,只需要把print b改为yield b就可以了:1
2
3
4
5
6
7
8def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
6) fib(
<generator object fib at 0x104feaaa0>
函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
1 | def odd(): |
5. map&reduce&filter&sorted
1 | 1, 2, 3, 4, 5, 6, 7, 8, 9]) map(str, [ |
reduce把一个函数作用在一个序列[x1, x2, x3…]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算
1 | reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4) |
filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。1
2
3
4
5def not_empty(s):
return s and s.strip()
filter(not_empty, ['A', '', 'B', None, 'C', ' '])
# 结果: ['A', 'B', 'C']
sorted()函数也是一个高阶函数,它还可以接收一个比较函数来实现自定义的排序。比如,如果要倒序排序,我们就可以自定义一个reversed_cmp函数:1
2
3
4
5
6def reversed_cmp(x, y):
if x > y:
return -1
if x < y:
return 1
return 0
传入自定义的比较函数reversed_cmp,就可以实现倒序排序:1
236, 5, 12, 9, 21], reversed_cmp) sorted([
[36, 21, 12, 9, 5]
6. lambda
1 | lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]) map( |
通过对比可以看出,匿名函数lambda x: x * x实际上就是:
1 | def f(x): |
关键字lambda表示匿名函数,冒号前面的x表示函数参数。
7. 装饰器
定义一个能打印日志的decorator:1
2
3
4
5def log(func):
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
观察上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:1
2
3
def now():
print '2013-12-25'
调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:1
2
3 now()
call now():
2013-12-25
把@log放到now()函数的定义处,相当于执行了语句:1
now = log(now)
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:1
2
3
4
5
6
7
8
9
10
11
12
13def log(text):
def decorator(func):
def wrapper(*args, **kw):
print '%s %s():' % (text, func.__name__)
return func(*args, **kw)
return wrapper
return decorator
def now():
print '2013-12-25'
执行结果如下:1
2
3 now()
execute now():
2013-12-25
和两层嵌套的decorator相比,3层嵌套的效果是这样的:1
'execute')(now) now = log(
1 | now.__name__ |
因为返回的那个wrapper()函数名字就是’wrapper’,所以,需要把原始函数的name等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。
不需要编写wrapper.__name__ = func.__name__
这样的代码,Python内置的functools.wraps
就是干这个事的,所以,一个完整的decorator的写法如下:1
2
3
4
5
6
7
8
9
10import functools
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print '%s %s():' % (text, func.__name__)
return func(*args, **kw)
return wrapper
return decorator
8. 偏函数
int()函数可以把字符串转换为整数,当仅传入字符串时,int()函数默认按十进制转换:1
2
3
4
5
6'12345') int(
12345
'12345', base=8) int(
5349
'12345', 16) int(
74565
定义一个int2()的函数,默认把base=2传进去:1
2
3
4
5
6
7def int2(x, base=2):
return int(x, base)
'1000000') int2(
64
'1010101') int2(
85
functools.partial
就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2:1
2
3
4
5
6import functools
2) int2 = functools.partial(int, base=
'1000000') int2(
64
'1010101') int2(
85
所以,简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
注意到上面的新的int2函数,仅仅是把base参数重新设定默认值为2,但也可以在函数调用时传入其他值:1
2'1000000', base=10) int2(
1000000
最后,创建偏函数时,实际上可以接收函数对象、*args和**kw这3个参数,当传入:1
int2 = functools.partial(int, base=2)
实际上固定了int()函数的关键字参数base,也就是:1
2kw = { base: 2 }
int('10010', **kw)
当传入:1
max2 = functools.partial(max, 10)
实际上会把10作为*args的一部分自动加到左边,也就是:1
max2(5, 6, 7)
相当于:1
2args = (10, 5, 6, 7)
max(*args)
结果为10。
10. 错误&调试
Exception hierarchy错误等级关系
1 | try: |
Python的错误其实也是class,所有的错误类型都继承自BaseException
捕获错误目的只是记录一下,便于后续追踪。但是,由于当前函数不知道应该怎么处理该错误,所以,最恰当的方式是继续往上抛,让顶层调用者去处理。
raise语句如果不带参数,就会把当前错误原样抛出。此外,在except中raise一个Error,还可以把一种类型的错误转化成另一种类型:
1 | try: |
断言
1 | # err.py |
assert
的意思是,表达式n != 0
应该是True,否则,后面的代码就会出错。
如果断言失败,assert语句本身就会抛出AssertionError:1
2
3
4$ python err.py
Traceback (most recent call last):
...
AssertionError: n is zero!
启动Python解释器时可以用-O
参数来关闭assert, 关闭后所有assert语句可当做pass。
logging
和assert比,logging不会抛出错误,而且可以输出到文件
1 | # err.py |
logging的好处是它允许你指定记录信息的级别,有debug
,info
,warning
,error
等几个级别,当我们指定level=INFO时,logging.debug就不起作用了。同理,指定level=WARNING后,debug和info就不起作用了。
可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。
logging的另一个好处是通过简单的配置,一条语句可以同时输出到不同的地方,比如console和文件。
调试器pdb
1 | # err.py |
1 | $ python -m pdb err.py |
pdb.set_trace()
这个方法也是用pdb,但是不需要单步执行,我们只需要import pdb
,然后,在可能出错的地方放一个pdb.set_trace()
,就可以设置一个断点。运行代码,程序会自动在pdb.set_trace()暂停并进入pdb调试环境,可以用命令p查看变量,或者用命令c继续运行。
1 | import pdb |
1 | $ python err.py |
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000