Python学习笔记基础
基础
- 语法
#
开头,注释- 缩进的语句视为代码块
- 大小写敏感
数据类型和变量
True
和False
首字母大写and
、or
和not
- 空值:
None
字符串格式化
%
%d
整数%f
浮点数%s
字符串%x
十六进制整数补位
'%2d-%02d' % (3, 1)' 3-01''%.2f' % 3.1415926'3.14'
数组:
list
和tuple
list
数组len(list)
得到长度list[-2]
获得倒数第二个元素list.append(ele)
往list中追加元素到末尾list.insert(1, ele)
,把元素插入到指定的位置,比如索引号为1
的位置list.pop()
,删除list末尾的元素,用pop()
方法list.pop(i)
删除指定位置的元素,用pop(i)
方法,其中i
是索引位置- 元素的数据类型也可以不同,
L = ['Apple', 123, True]
tuple
数组:classmates = ('Michael', 'Bob', 'Tracy')
tuple
一旦初始化就不能修改,代码更安全
条件判断和循环
if
elif
else
for in
while
range()
函数1,5) #代表从1到5(不包含5)range([1, 2, 3, 4]1,5,2) #代表从1到5,间隔2(不包含5)range([1, 3]5) #代表从0到5(不包含5)range([0, 1, 2, 3, 4]raw_inpit(str)
读取的内容永远以字符串的形式返回
dict
和set
dict
就是map
,用key-value的形式存储。d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}根据
key
获得value
[]
:一旦key
不存在就会报错get()
函数:如果key不存在,可以返回None,或者自己指定的value(作为第二个参数传入)
set
是一组key的集合,但不存储value,key不能重复。- 需要list作为输入
add(key)
函数用来往里面添加元素,自动忽略重复remove(key)
函数用来删除元素&
操作用来做交集|
操作用来做并集
函数
Python内置数据类型转换函数:如
int()
Python可以给函数赋别名,如:
a = abs
定义函数,
def
:def my_abs(x):if x >= 0:return xelse:return -x如果没有
return
语句,函数执行完毕后也会返回结果,只是结果为None
。return None
可以简写为return
。空函数,
pass
:def nop():pass类型检查,
isinstance
:def my_abs(x):if not isinstance(x, (int, float)):raise TypeError('bad operand type')if x >= 0:return xelse:return -xPython函数可以返回多个值:
import mathdef move(x, y, step, angle=0):nx = x + step * math.cos(angle)ny = y - step * math.sin(angle)return nx, ny100, 100, 60, math.pi / 6)x, y = move(print x, y151.961524227 70.0事实上,它并没有返回多个值,而是返回了一个tuple
Python具有默认参数:
def power(x, n=2):s = 1while n > 0:n = n - 1s = s * xreturn s默认参数必须指向不变对象,不然默认参数就会不断变化,例子:
def add_end(L=[]):L.append('END')return Ladd_end()['END']add_end()['END', 'END']add_end()['END', 'END', 'END']可变参数,
*
:def calc(*numbers):sum = 0for n in numbers:sum = sum + n * nreturn sum1, 2)calc(5calc()0在函数内部,参数
numbers
接收到的是一个tuple。如果已经有一个list或者tuple,可以这么写:
1, 2, 3]nums = [calc(*nums)14关键字参数:
**
,会把多与参数自动组装成一个dict
:def person(name, age, **kw):print 'name:', name, 'age:', age, 'other:', kw'Michael', 30)person(name: Michael age: 30 other: {}'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'}'city': 'Beijing', 'job': 'Engineer'}kw = {'Jack', 24, **kw)person(name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}参数定义的顺序必须是:必选参数、默认参数、可变参数和关键字参数
高级特性
切片(Slice )
|
L[0:3]
表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引0,1,2,正好是3个元素。- 如果第一个索引是0,还可以省略。
L[-1]
取倒数第一个元素,也支持倒数切片:L(-2:)
- 只写
[:]
就可以原样复制一个list L[:10:2]
表示前十个元素,每两个取一个:[0,2,4,6,8]
- tuple也可以用切片,操作结果也是tuple
- 字符串也支持切片
迭代(Iteration)
只要是可迭代对象(list,tuple,dict,set,字符串)都可以用
for...in...
迭代默认情况下,dict迭代的是key。
- 如果要迭代value,可以用
for value in d.itervalues()
- 如果要同时迭代key和value,可以用
for k, v in d.iteritems()
。
- 如果要迭代value,可以用
判断一个对象是否是可迭代对象:
from collections import Iterableisinstance('abc', Iterable) # str是否可迭代 Trueisinstance([1,2,3], Iterable) # list是否可迭代 Trueisinstance(123, Iterable) # 整数是否可迭代 False拥有下标的循环:
for i, value in enumerate(['A', 'B', 'C']):print i, valuefor
循环同时引用两个变量:for x, y in [(1, 1), (2, 4), (3, 9)]:print x, y
列表生成式(List Comprehensions)
[x * x for x in range(1, 11)]
→[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
- 加上判断:
[x * x for x in range(1, 11) if x % 2 == 0]
→[4, 16, 36, 64, 100]
- 两层循环(可以用来生成全排列):
[m + n for m in 'ABC' for n in 'XYZ']
→['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
生成器(Generator)
生成器里面装了用来生成一个list的算法,这样就不必创建完整的list,从而节省大量的空间,一边循环,一边计算。
|
也可以用for
循环:
|
定义generator的另一种方法,
yield
:# print Fibonacci listdef fib(max):n, a, b = 0, 0, 1while n < max:print ba, b = b, a + bn = n + 1改写成generator:
# Fibonacci generatordef fib(max):n, a, b = 0, 0, 1while n < max:yield ba, b = b, a + bn = n + 16)fib(<generator object fib at 0x104feaaa0>generator的执行顺序:
变成generator的函数,在每次调用
next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。def odd():print 'step 1'yield 1print 'step 2'yield 3print 'step 3'yield 5...o = odd()o.next()step 11o.next()step 23o.next()step 35o.next()Traceback (most recent call last):File "<stdin>", line 1, in <module>StopIteration
函数式编程(Functional Programming)
Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。
高阶函数
map
第一个参数是函数,第二个参数是可迭代对象,返回一个listreduce
第一个参数是函数,第二个参数是可迭代对象,函数的两个参数(result, item)filter
sorted
第一个参数是可迭代对象,第二个参数是比较函数(有默认值)
返回函数
- Python也有闭包的概念
匿名函数(lambda)
lambda x: x * x
等同于:
|
装饰器(Decorator)
@
|
|
相当于执行了:
|
但此时,now.__name__
已经被替换成了wrapper
,所以完整的写法:
|
偏函数(Partial function)
可以固定某个参数的默认值,比如下方的例子,固定了int
函数的base
参数。
|
模块
模块:一个文件就是一个模块
包:就是不同的文件夹,但每个包目录下面都要有一个__init__.py
的文件,否则就是一个普通目录而不是一个包。__init.__.py
可以是空文件,也可以有代码,因为__init__.py
本身就是一个模块,它的模块名就是它的父目录。比如如下的文件结构:
文件www.py
的模块名就是mycompany.web.www
,两个文件utils.py
的模块名分别是mycompany.utils
和mycompany.web.utils
。
引入模块
|
第1行和第2行是标准注释,第1行注释可以让这个hello.py
文件直接在Unix/Linux/Mac上运行,第2行注释表示.py文件本身使用标准UTF-8编码;
第4行是一个字符串,表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释;
第6行使用__author__
变量把作者写进去,这样当你公开源代码后别人就可以瞻仰你的大名;
sys.argv
用list存储了命令行的所有参数。python hello.py
对应['hello.py']
,python hello.py Michael
对应 ['hello.py', 'Michael]
。
最后两行:当用命令行运行hello
模块时,Python解释器把一个特殊变量__name__
置为__main__
,而如果在其他地方导入该hello
模块时,则不会。
别名:
cStringIO
是C写的,速度更快,这样就可以优先导入cStringIO
。如果有些平台不提供cStringIO
,还可以降级使用StringIO
。try:import cStringIO as StringIOexcept ImportError: # 导入失败会捕获到ImportErrorimport StringIO还有类似
simplejson
这样的库,在Python 2.6之前是独立的第三方库,从2.6开始内置try:import json # python >= 2.6except ImportError:import simplejson as json # python <= 2.5作用域:
前缀
_
表示私有函数,不应该被外部引用。
安装第三方模块
pip
pip install
是全局安装
默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块。搜索路径存放在sys
模块的path
变量中,可以添加要搜索的目录:
|
__future__
模块
Python提供了__future__
模块,把下一个新版本的特性导入到当前版本,于是我们就可以在当前版本中测试一些新版本的特性,比如:
|
|
面向对象编程
|
类和实例
|
class
后面紧接着是类名,即Student
,类名通常是大写开头的单词,紧接着是(object)
,表示该类是从哪个类继承下来的。
__init__
函数是构造函数。
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self
,并且,调用时,不用传递该参数。
访问限制
- 在Python中,实例的变量名如果以
__
开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。 - 变量名类似
__xxx__
的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__
、__score__
这样的变量名。 - 以一个下划线开头的实例变量名,比如
_name
,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。 - 双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问
__name
是因为Python解释器对外把__name
变量改成了_Student__name
,所以,仍然可以通过_Student__name
来访问__name
变量
继承与多态
OOP的特性,不作多余解释。
获取对象信息
type()
123)type(<type 'int'>'str')type(<type 'str'>None)type(<type 'NoneType'>type(abs)<type 'builtin_function_or_method'>type(a)<class '__main__.Animal'>types
模块types.StringType
、types.UnicodeType
、types.ListType
、types.TypeType
(所有类型本身的类型就是TypeType
)isinstance()
# object -> Animal -> Dog -> Huskya = Animal()d = Dog()h = Husky()isinstance(h, Husky) # Trueisinstance(h, Dog) # Trueisinstance(h, Animal) # True判断一个变量是否是某些类型中的一种
isinstance('a', (str, unicode)) # Trueisinstance(u'a', (str, unicode)) # Truedir()
:获得对象的所有属性和方法,返回一个list类似
__xxx__
的属性和方法在Python中都是有特殊用途的。比如__len__
方法返回长度,实际上,在len()
函数内部,它自动去调用该对象的__len__()
方法:class MyObject(object):def __len__(self):return 100obj = MyObject()len(obj) # 100hasattr
、setattr
、getattr
hasattr(obj, 'x') # 有属性'x'吗?setattr(obj, 'y', 19) # 设置一个属性'y'getattr(obj, 'z') # 获取属性'z'
__slots__
给class绑定新的方法,
MethodType
:def set_score(self, score):self.score = scoreStudent.set_score = MethodType(set_score, None, Student)限制类能添加的属性,
__slots
:class Student(object):__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称对子类不起作用。
@property
|
|
不定义setter的话,就是一个只读属性
多重继承
|
定制类
__str__()
方法用于自动类型转换class Student(object):def __init__(self, name):self.name = namedef __str__(self):return 'Student object (name: %s)' % self.name__iter__()
,配合next()
方法,可以对类进行for...in
操作。class Fib(object):def __init__(self):self.a, self.b = 0, 1 # 初始化两个计数器a,bdef __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 # 返回下一个值for n in Fib():print n__getitem__()
:可以类似list那样,用方括号[]
取出对应的元素。class Fib(object):def __getitem__(self, n):a, b = 1, 1for x in range(n):a, b = b, a + breturn af = Fib()f[0] # 1f[1] # 1f[10] # 89f[:10] # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]f[:10:2] # 未对step做处理,[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]__getitem__()
也没有对负数进行处理。__getitem__()
也可以将对象处理成一个dict:class Test(object):def __init__(self, a, b):self.a = aself.b = bdef __getitem__(self, n):if n == 'a':return self.aelse:return self.bf = Test('aa', 'bb')print f['a'] # 'aa'与之对应的是
__setitem__()
方法,把对象视作list或dict来对集合赋值。还有一个
__delitem__()
方法,用于删除某个元素。__getattr__()
,用于处理对象没有的属性或方法:class Test(object):def __init__(self, name):self.name = namedef __getattr__(self, attr):print 'Test doesn\'t have attribute: %s' % attrtest = Test('aa')test.age # Test doesn't have attribute: age同样也可以返回方法。
只有在没有找到属性的情况下,才调用
__getattr__
。__call__()
使得实例本身可以称为一个函数。class Student(object):def __init__(self, name):self.name = namedef __call__(self, age):self.age = ageprint('My name is %s and my age is %s' % self.name, age)student = Student('Jackie')student(25) # My name is Jackie and my age is 25可以用
callbale()
来判断一个对象是否能被调用。
元类
type()
不仅可以用来查看某个对象的类型,也可以用来创建新的类:def fn(self, name='world'): # 先定义函数print('Hello, %s.' % name)Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello classh = Hello()h.hello() # Hello, world.print(type(Hello)) # <type 'type'>print(type(h)) # <class '__main__.Hello'>传入三个参数:
- 类名
- 继承的父类的tuple
- 属性和方法的dict
通过
type()
函数创建的类和直接写class是完全一样的metaclass
元类,暂时不学
错误、调试和测试
错误处理
try...except...finally...
|
except
还会捕获异常的子类,所有的错误类型都继承自BaseException
,所以在使用except
时需要注意的是,它不但捕获该类型的错误,还把其子类也“一网打尽”。
|
logging
Python内置的logging
模块可以非常容易地记录错误信息。
|
|
自己编写错误
|
继续向上抛出异常
|
调试
断言assert
|
assert
语句本身会抛出异常,AssertionError
可以用-O
参数关闭assert:python -O err.py
日志logging
几个级别:debug
,info
,warning
,error
。
可以指定级别,当我们指定level=INFO
时,logging.debug
就不起作用了。
|
单元测试
unittest
模块
|
测试类:从
unittest.TestCase
继承。以
test
开头的方法才会被认为是测试方法,才能被执行用断言来判断是否符合期望,
assertEquals()
assertTrue()
,以及抛出异常:with self.assertRaises(AttributeError):value = d.empty
运行单元测试
在单元测试文件
mydict_test.py
下加上两行代码:if __name__ == '__main__':unittest.main()这样就可以像正常脚本一样运行了
$ python mydict_test.py
用参数
-m unittest
:$ python -m unittest mydict_test.py
setUp()
和tearDown()
分别会在每调用一个测试方法的前后分别被执行。
文档测试
doctest
模块:
|
运行测试:python mydict.py
IO编程
文件读写
|
由于文件读写时都有可能产生IOError
,一旦出错,后面的f.close()
就不会调用。最好用try...finally
来写:
|
Python引入了with
语句来帮助调用:
|
read(size)
可以读取 size个字节的内容readline()
可以读取一行内容readlines()
一次读取所有内容并按行返回list
操作文件和目录
os
模块
|
os
模块不提供拷贝文件的接口,而shutil
模块提供了copyfile()
的函数。
序列化(pickling)
序列化:把变量从内存中变成可存储或传输的过程。
cPickle
和pickle
|
|
JSON
内置的json
模块
|
定制JSON序列化
将某个class的实例序列化成JSON:
|
- 偷懒的方法:利用
__dict__
属性,它就是一个dict
,用来存储实例变量。
反序列化:
|
进程和线程
多进程(multiprocessing)
Unix/Linux操作系统提供了一个fork()
系统调用,子进程永远返回0
,而父进程返回子进程的ID。子进程只需要调用getppid()
就可以拿到父进程的ID。
|
multiprocessing
模块 - 跨平台
|
|
Pool 进程池
|
Pool
默认大小是CPU核数,当创建多于Pool
大小的进程数时,后面的进程就会等待前面的进程执行完成再执行。
|
也可以设置p = Pool(5)
同时运行5个进程。
进程间通讯
Queue
策略
|
还有Pipes
等策略
多线程
threading
高级模块对thread
模块进行了封装,较常使用。
|
|
Lock
|
多核CPU
Python因为存在global interpret lock(GIL锁)的关系,虽然可以多线程执行,但无法真正利用多核。每个进程最多利用2两个核。
ThreadLocal
为每个线程单独建立作用域
|