https://www.liaoxuefeng.com/wiki/1016959663602400
本文是根据廖雪峰python教程学习总结而来。
参考《python学习手册》,《流畅的python》以及python官方手册等网络资料
略过了与C和C++ 语言相同的语法部分
python的字符串可以用双引号和单引号,作用是相同的。转义的方式和C差不多。不同的是,如果想让一个字符串里面的字符都不用转义的话,可以在字符串前面加字母r来声明。
例如
r'\\\\t\t\t\t\'
里面的内容都无需转义。
或者使用3个引号来把字符串包起来,这样也不需要转义了,而且中间还可以换行。
例如
'''123\\\t\t\t\'''
和C语言一样,都是使用百分号占位,但是不同的是,python里面占位符对应的数据也需要百分号来标志,而且占位符对应的数据与字符串之间不需要加逗号。
例如
print ("我叫%s,今年%d岁,工作%d年了" %('郑德伦', 27, 2))
还有一种新的占位符使用{0}占位,有点像C#的格式化字符串操作。
python官方手册str.format部分
print('{0:,.3f} {1},{name}, {2},'.format(99999.12345, '嘎嘎',[1,2,3,4], name='123'))
输出:
99,999.123 嘎嘎,123, [1, 2, 3, 4],
True False表示真假,布尔运算是and or not,这点与C语言不同
与C语言不同的是,if和else后面都加冒号,else if变成了elif
a = 10
b = 20
if a > b :
print (a > b)
elif a == b:
print (a == b)
else:
print (a < b)
C语言中,两个整数相除的结果还是整数,而在python中,结果是一个浮点数。
如果想让两个整数相除的结果变成整数的话,需要用地板除的方式,双斜线
例如:
10//3
结果是3
list有点像是C++ 中的vector,可以动态的添加和删除元素。是一个有序的表。可以对一个list使用len来获取长度。与C++ 不同,C++ 是强类型的语言,vector中只能包含相同类型的元素,而python的list中可以包含不同类型的对象,还可以包含另一个list。
list使用中括号来表示
例如:
['123', '234', '345]
通过索引访问list时,还可以使用负数,-1就是取最后一个元素
a=['123', '234', '345']
a[-1]
输出345
在后面追加元素,相当于C++ 中vector的push_back,在python中是append
例如
a.append('666')
在中间插入元素,相当于C++ 的insert,在python也是insert
例如a.insert(1, ‘777’) 在索引号1的地方插入’777’
在末尾删除元素,相当于C++ 的pop_back,在python中是pop
例如
a.pop()
删除指定索引的元素,在C++ 中无法通过索引直接删除vector的元素,需要使用迭代器,在python中也用pop删除指定位置的元素
例如
a.pop(1)
删除索引1的元素
tuple类似于C++ 中的tuple,和python的list也很像,只不过tuple一旦创建就不能更改里面的内容了。
tuple可以隐式转换为单个变量例如:
a, b, c = (1, 2, 3)
print(a, b, c)
输出:
1 2 3
也可以使用封包
first, *rest = (1,2,3,4)
print(first)
print(rest)
输出
1
[2, 3, 4]
输出函数和C语言类似使用
print ("hello world")
输入函数与C和C++ 都比较不同,使用input来获取输入的内容
例如
name = input("input your name")
在C和C++ 中数字转字符串比较麻烦,一般在C语言中可以使用sprintf。C++ 直接使用to_string。
字符串转数字的话,C语言可以使用atoi,C++ 可以使用stringstream。
在python中可以直接使用int() float() str()转换
例如:
a = int('123')
b = float('12.3)
c = str(123.3)
python中有两种循环,一种是for循环一种是while循环。和C、C++ 中的不太一样。没有do while循环
for循环:类似于C++中的for (auto i : vec)这种range-based的for循环
for x in range(10):
print(x)
while循环:和C、C++中的while循环类似
while x < 10:
print(x)
x += 1
python的循环中同样可以使用continue和break来终止循环
字典类似与C++ 中的unordered_map,是一种哈希表的key-value的查找结构。
使用大括号来表示:
score = {"Mike":100, "冰封飞飞":100, "Fvck":88}
判断一个key在不在字典里面可以使用
“冰封飞飞” in score
返回结果是True或者False
删除一个元素使用pop方法。score.pop(“冰封飞飞”)
插入一个元素和C++一样直接可以score[“new”] = 100
查找操作可以使用get操作,因为如果key不存在的时候,直接使用score[“nonexist”]会报错。
get操作还可以指定默认值,如果查找不到key的话,返回默认值
s = {}
print(s.get('a', '3'))
结果:
3
for (k, v) in score.items():
print("%s:%d" % (k, v))
集合类似与C++ 中的unordered_ser,是一种哈希表的集合结构。
如果要创建set,需要提供一个list作为输入集合。
s = set([1, 2, 2, 3]) 初始化时,自动会去重元素。
结果是s = {1, 2, 3}
添加元素使用add方法,删除元素使用remove方法
s.add(4)
s.remove(4)
集合类似于数学上的集合,有交集和并集的操作,
s1 & s2, s1 | s2
函数名是一个指向函数对象的引用,下面可以直接将函数赋值一个别名
a = abs
a(-10)
而且函数名本身也是一个变量,可以赋值
abs = 10
abs(-10)
这样执行的话,abs(-10)就会报错了。
python包含了很多内置的函数,可以从python官方手册查看
函数的定义使用def
def myFunc(a, b):
return a + b
空语句可以使用pass,类似于C/C++ 中的分号
可变参数:在参数前面加一个星号,就变成了可变参数,可以传入任意个数的参数,实际上是自动封装成了一个tuple。
def myFunc(*num):
result = 0
for i in num:
result += i
return result
调用时可以myFunc(1, 2, 3)这样调用,
也可以传入一个list或者tuple
param = [1, 2, 3]
myFunc(*param)
def func(a, b, **kw):
print(kw)
这个函数里面第三个参数是**kw,实际上是一个字典。
调用时可以这样调用:func(1, 2, name=“123”, age=18)
参数传进去时,实际上a = 1, b = 2 kw = {“name”:“123”, “age”:18}
也可以直接传一个dict作为第三个参数,但是前面需要加两个星号
kw = {"name":"123", "age":18}
func(1, 2, **kw)
关键字参数必须跟在位置参数后面。下面的调用方式是不合法的。
def func(a, b=1, c=2):
print(a, b, c)
func(a=1, 2)
def func(a, b, *, name, job):
print(name)
print(job)
在上面关键字参数,无法控制**kw传入的key是什么。使用关键字参数可以控制可以传入哪些key。需要使用一个星号作为分隔符。星号后面定义可以传入的key的名称。
func(1, 2, name="1", job=2) -> OK
func(1, 2, job = 3) -> ERROR
这个星号必不可少,如果正好有一个可变参数,那就不需要额外的星号了
def func(a, b, *args, name, job):也是可以的
定义函数的时候可以使用多重参数组合,定义的顺序是,必选参数,默认参数,可变参数,命名关键字参数和关键字参数
def f1(a, b = 0, *args, **kw)
def f2(a, b = 0, *, d, **kw)
对于任意的函数都可以使用一个tuple和一个dict来组织参数调用。类似于func(*args, **kw)
函数注释是3.X新加入的功能,可以给函数每一个参数添加一个注释,以及标记返回值的类型。
def func(a:'spam', b:(1,10),c:float=2.5) -> int:
return a + b + c
注解实际上是把上面添加的内容,写入到了函数的__annotations__方法中。
可以使用
print(func.__annotations__)
打印函数注解,结果如下
{'a': 'spam', 'b': (1, 10), 'c': , 'return': }
lambda比def功能要小,lambda仅能编写简单的函数,连if也不能使用,也不能写return,返回值就是表达式的结果
f = lambda x, y, z : x + y + z
装饰器是一种委托的调用方式,将被装饰的函数包装一层,增加一些定制处理。
装饰器的用法如下,@log是一个语法糖,相当于test = log(test),将test函数替换为wrapper函数。
@functools.wraps(func)是系统帮忙做的一些处理,可以让装饰后的函数和原函数看起来一致。例如:包装后的函数__name__还是显示和原函数一致,不会显示为wrapper函数的__name__
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print(''.format(time.ctime(), func.__name__, args, kw))
return func(*args, **kw)
return wrapper
@log
def test(a):
print('test func:{0}'.format(a))
def main():
test(5)
if __name__ == '__main__':
main()
结果:
test func:5
偏函数类似与C++ 中的bind,可以将函数的某些参数固定下来,生成一个新的函数。
import functools
int2 = functools.partial(int, base=2)
print(int2('10101'))
结果:
21
通过切片操作可以很方便的将list,tuple,string中一段对象取出来。python没有提供substr的函数,字符串的取子串的操作都使用切片来进行
L = list(range(10))
print("L = %s " % L)
print("L[0:3] = %s " % L[0:3])
print("L[:3]= %s " % L[:3])
print("L[-2:] = %s " % L[-2:])
print("L[-2:-1] = %s " % L[-2:-1])
print("L[:10:2] = %s " % L[:10:2])
print("L[:] = %s " % L[:])
结果:
L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
L[0:3] = [0, 1, 2]
L[:3]= [0, 1, 2]
L[-2:] = [8, 9]
L[-2:-1] = [8]
L[:10:2] = [0, 2, 4, 6, 8]
L[:] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
切片是一个左闭右开的区间,例如L[0:3],获取[0, 3)区间的元素。
第二个冒号之后的数字是步长,可以相隔步长来取数
只包含一个冒号就是获取变量本身
python的迭代使用for,10循环中说过一些for的内容。类似于C++ 的for(auto i : vec)这种形式的迭代。不是基于下标迭代。
d = {'a' : 1, 'b' : 2}
for key in d:
print (key)
for value in d.values():
print (value)
for k, v in d.items():
print('%s : %d' % (k, v))
输出结果:
a
b
1
2
a : 1
b : 2
对于字典,for迭代只会迭代key,如果迭代value的话需要使用d.values()作为迭代对象。如果迭代key-value的话,需要使用d.items()作为迭代对象
判断一个变量是否能迭代,使用下面代码判断。
from collections.abc import Iterable
d = {'a' : 1, 'b' : 2}
print(isinstance(d, Iterable))
print(isinstance(1, Iterable))
输出
True
False
使用enumerate可以将一个可迭代的对象转换为下标+对象的tuple来迭代
from collections.abc import Iterable
d = {'a' : 1, 'b' : 2}
for i in enumerate(d.items()):
print(i)
输出
(0, ('a', 1))
(1, ('b', 2))
这个迭代器我用的是i一个元素,这时候i就变成了一个tuple,如果使用两个元素的话,会将这个tuple拆分到两个元素上面去
例如改为:
for i, v in enumerate(d.items()):
print('%s : %s' % (i, v))
结果就是
0 : ('a', 1)
1 : ('b', 2)
对参数进行排序
L = [3, 2, 4, 1]
L = sorted(L)
print (L)
结果
[1, 2, 3, 4]
将参数相同索引的数据组成一个tuple,如果几个参数的数量不同,按最小的算
L = [1, 2, 3, 4]
S = ['a', 'b', 'c', 'd', 'e']
Z = zip(L, S)
print (list(Z))
结果
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]
将参数返回一个索引+数据的tuple
S = ['a', 'b', 'c', 'd', 'e']
print (list(enumerate(S)))
结果
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]
将可迭代对象按照参数1,进行过滤。
def isInt(x):
if isinstance(x, int):
return True
else:
return False
def main():
S = [1, 2, 3.0, 4.1, '5', (6,)]
print (list(filter(isInt, S)))
if __name__ == "__main__":
main()
结果
[1, 2]
使用参数1中的函数,对后面的参数进行迭代运算,如果后面迭代对象数量不一致,按最小的算。
def add(x, y):
return x + y
def main():
S = [1, 2, 3.0, 4.1]
L = [2, 3, 4, 5, 6]
print (list(map(add, S, L)))
if __name__ == "__main__":
main()
结果
[3, 5, 7.0, 9.1]
使用参数1中的函数,对后面的参数进行累加运算,得到一个结果。
from functools import reduce
def main():
d = reduce(lambda x, y : x + y, range(101))
print(d)
if __name__ == "__main__":
main()
结果
5050
对参数求和
对参数全部进行or运算
对参数全部进行and运算
求参数最大值
求参数最小值
使用range可以生成一个范围的数据集合,但是range的返回值是Object对象,需要转换为其他对象才可以使用。例如生成一个list
print(list(range(1, 11)))
结果为:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
L = [ m * n for m in range(1, 10) for n in range(1, 10) if m >= n]
print(L)
结果为:
[1, 2, 4, 3, 6, 9, 4, 8, 12, 16, 5, 10, 15, 20, 25, 6, 12, 18, 24, 30, 36,
7, 14, 21, 28, 35, 42, 49, 8, 16, 24, 32, 40, 48, 56, 64, 9, 18, 27, 36,
45, 54, 63, 72, 81]
可以用一句话来使用嵌套循环和条件判断语句以及一个表达式来生成一个list,同样也可以使用相同的方法来生成一个dict,一个set
将列表生成式的中括号换成大括号,然后将表达式换成一个key:value的形式就可以变成字典生成式了。
L = { ('%dx%d' % (m, n)) :(m * n) for m in range(1, 10) for n in range(1, 10) if m <= n}
cntLine = 1
for i, v in enumerate(L.items()):
print(v[0], '=', v[1], end=" ")
if (v[1] % 9 == 0) and (v[1] / 9 == cntLine):
print()
cntLine += 1
结果:
1x1 = 1 1x2 = 2 1x3 = 3 1x4 = 4 1x5 = 5 1x6 = 6 1x7 = 7 1x8 = 8 1x9 = 9
2x2 = 4 2x3 = 6 2x4 = 8 2x5 = 10 2x6 = 12 2x7 = 14 2x8 = 16 2x9 = 18
3x3 = 9 3x4 = 12 3x5 = 15 3x6 = 18 3x7 = 21 3x8 = 24 3x9 = 27
4x4 = 16 4x5 = 20 4x6 = 24 4x7 = 28 4x8 = 32 4x9 = 36
5x5 = 25 5x6 = 30 5x7 = 35 5x8 = 40 5x9 = 45
6x6 = 36 6x7 = 42 6x8 = 48 6x9 = 54
7x7 = 49 7x8 = 56 7x9 = 63
8x8 = 64 8x9 = 72
9x9 = 81
和字典生成式没啥区别,只是把表达式换成一个值就可以了
有列表,字典,集合生成式但是没有tuple生成式,如果想生成一个tuple的话,需要先生成一个list然后使用tuple()转换成一个tuple。因为小括号这个符号被生成器生成式占用了。
将上面的生成式中括号换成小括号就变成了生成式,生成式是一种特殊的函数,使用next操作来取下一次的值,不会将所有值全部计算出来,而是用到的时候再做计算,节省内存。
g = (x * x for x in range(1, 10))
print (next(g))
print (next(g))
print (next(g))
print (next(g))
输出:
1
4
9
16
同时,生成式也是一个可迭代的对象,可以使用for来迭代
g = (x * x for x in range(1, 10))
for i in g:
print (i)
输出:
1
4
9
16
25
36
49
64
81
yeild的字典含义是产出和让步,对于python来说,这两个含义都成立 yeild b,这个语句会产出一个值,给next(…)的调用方,此外还会做出让步,暂停执行生成器,让调用方继续工作。
def fib(num):
cnt, b, c = 0, 0, 1
while cnt < num:
yield b
b, c = c, b + c
cnt += 1
print('done')
f = fib(6)
print(next(f))
print(next(f))
print(next(f))
print(next(f))
print(next(f))
print(next(f))
结果:
0
1
1
2
3
5
yeild类似于一个不完全的return,可以保存状态。每次到yeild时,程序会返回,然后使用next执行的话,可以从本次yeild的位置继续执行
yeild是给调用者返回一个值,而send是调用者给函数发送一个值。x = yeild i这种情况下,使用send(N)之后,x就被赋值成了N。send必须在调用完至少一次next操作时,才可调用。要确保函数的语句执行到yeild处。
def test():
for i in range(10):
x = yield i ** 2
print("x={0}".format(x))
def main():
G = test()
print(next(G))
print(G.send(5))
print(next(G))
print(next(G))
结果:
0
x=5
1
x=None
4
x=None
9
在生成器中的return the_result
语句会抛出StopIteration(the_result)异常,这样调用方可以用异常的value属性中获取the_result。
可以被for循环迭代的对象都叫做可迭代对象,可以使用isinstance Iterfable来判断对象是否可以被for循环迭代
from collections.abc import Iterable
isinstance(obj, Iterable)
除了被for循环迭代,还有一些对象可以使用next迭代。可以使用isinstance Iteraotr来判断是否可以被next迭代
from collections.abc import Iterator
isinstance(obj, Iterator)
使用iter()可以将list,dict等Iterable对象变成Iterator的
L = list(range(10))
Iter = iter(L)
print (next(Iter))
print (next(Iter))
有些对象支持单个迭代器,有些支持多个迭代器。单个迭代器是指,使用iter获取该对象的迭代器时,如同单例模式一样,多次获取都是获取同一个迭代器。迭代器1增加,迭代器2也会同时增加
例如range()支持多个迭代器,内置的list等也支持多个迭代器:
R = range(3)
R = range(3)
I1 = iter(R)
I2 = iter(R)
print(next(I1))
print(next(I2))
print(next(I2))
结果:
0
0
1
zip,map,filter,生成器不支持多个迭代器
Z = zip((1, 2, 3), (10, 11, 12))
I1 = iter(Z)
I2 = iter(Z)
print(next(I1))
print(next(I1))
print(next(I2))
结果:
(1, 10)
(2, 11)
(3, 12)
闭包的数学含义是能够读取其他函数变量的函数。在python中在一个函数的内部定义另外一个函数,才可以访问这个函数内部的变量,所以在python中闭包可以理解为定义在函数内部的函数。
def createCounter():
'''
利用闭包返回一个计数器函数,每次调用它返回递增整数:
'''
num = 0
def counter():
nonlocal num
num += 1
return num
return counter
模块通常是一个文件,在python中,模块也是一个对象。模块内的变量,函数,类都是模块的属性。
python文件开头很常见的两个注释
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
在python中,一个模块如果是被python直接加载的话,__name__属性为’main’,如果是被其他模块加载的话,__name__就是模块的名称。python一般使用下面的代码作为main函数
if __name__=='__main__':
test()
python3的所有类都是继承自object基类的,也支持多重继承。
python中的类和C++中的类有点区别,python中的类也是一种对象,类的实例也是一个对象。
python中也没有私有属性的概念,需要用一些其他手法来实现。在属性名前面加双下划线是一种伪私有的概念,因为他把属性的名称替换成了_类名__属性名
这种表示形式。
class A(object):
def __init__(self):
self.name = 'A'
class C(object):
def __init__(self):
self.color = 'Red'
self.__private = 'C'
class B(A):
def __init__(self):
self.age = 50
A.__init__(self)
C.__init__(self)
def __str__(self):
result = ''
for (k,v) in self.__dict__.items():
result += '{0}={1}\r\n'.format(k, v)
return result
def main():
a = B()
print(a)
if __name__ == '__main__':
main()
结果:
age=50
name=A
color=Red
_C__private=C
由于python是动态类型的语言,类的属性可以使用代码在运行过程中动态的添加。如果想要限制用户添加的话,需要使用__slots__来实现。
class test:
__slots__ = ['age', 'name']
def __init__(self):
self.test = 10
结果:
File "/media/bingfengfeifei/数据/PyCode/helloworld.py", line 37, in __init__
self.test = 10
AttributeError: 'test' object has no attribute 'test'
实际上这种做法是把类的__dict__属性删除掉来实现的,动态添加和删除属性就是通过对类的__dict__属性来操作实现。
property是将类内的属性字段,包装成setter,getter函数处理,可以增加一些检查和限制。有两种定义属性的方式
class newprops:
def getage(self):
return self._age
def setage(self, value):
self._age = value
def delage(self):
del self._age
def __init__(self):
self._age = 0
age = property(getage, setage, delage, "help age") #get set del doc
class newprops2:
@property
def age(self):
return self._age
@age.setter
def age(self, value):
self._age = value
@age.deleter
def age(self):
del self._age
def __init__(self):
self._age = 0
def main():
a = newprops()
a.age = 20
del(a.age)
print(hasattr(a, 'age'))
print(help(a))
if __name__ == '__main__':
main()
结果:
False
Help on newprops in module __main__ object:
class newprops(builtins.object)
| Methods defined here:
|
| __init__(self)
| Initialize self. See help(type(self)) for accurate signature.
|
| delage(self)
|
| getage(self)
|
| setage(self, value)
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
|
| age
| help age
None
python中类的很多操作,都是重定向到一个双下划线开头和结尾的函数__X__,我们重定义这个函数之后,就可以拦截一些内置的操作。
方法 | 重载 | 调用 |
---|---|---|
__init__ | 构造函数 | 对象建立时 |
__del__ | 析构函数 | 对象删除时 |
__add__ | 运算符+ | X+Y,X+=Y(如果没有__iadd__) |
__or__ | 位或运算符 | X|Y X|=Y |
__repr__, __str__ | 打印,转换 | print(X), repr(X), str(X) |
__call__ | 函数调用 | X() |
__getattr__ | 点号运算 | X.undefined |
__setattr__ | 属性赋值语句 | X.any = value |
__delattr__ | 属性删除 | del X.any |
__getattribute__ | 属性获取 | X.any |
__getitem__ | 索引运算 | X[key],X[i:j] |
__setitem__ | 索引赋值语句 | X[key]=value |
__delitem__ | 索引和分片删除 | del X[key] |
__len__ | 长度 | len(X) |
__bool__ | 布尔测试 | bool(X) |
__lt__,__gt__,__le__,__ge__,__eq__,__ne__ | 特定的比较 | X < Y, X > Y, X <= Y等 |
__radd__ | 右侧加法 | Other+X |
__iadd__ | 原地加法 | X+=Y |
__iter__,__next__ | 迭代环境 | I=Iter(X), next(I) |
__contains__ | 成员关系测试 | item in X |
__index__ | 整数值 | hex(X), bin(X) |
__enter__,__exit__ | 环境管理器 | with obj as var: |
__get__,__set__,__delete__ | 属性描述符 | x.attr,x.attr=value,del x.attr |
__new__ | 创建 | 在__init__之间创建对象 |
from enum import Enum, unique
@unique
class WeekDay(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6
def main():
print(WeekDay['Mon'])
print(WeekDay.Sat)
print(WeekDay(2))
print('------------')
for k,v in WeekDay.__members__.items():
print(k,v,v.value)
结果:
WeekDay.Mon
WeekDay.Sat
WeekDay.Tue
------------
Sun WeekDay.Sun 0
Mon WeekDay.Mon 1
Tue WeekDay.Tue 2
Wed WeekDay.Wed 3
Thu WeekDay.Thu 4
Fri WeekDay.Fri 5
Sat WeekDay.Sat 6
元类是可以生成类的类。使用元类可以在运行时动态的创建一个类。元类需要继承type类型。
type可以查看一个变量的类型,type也可以动态的创建一个类。
参数1:类名
参数2:基类
参数3:方法dict
Hello = type('Hello', (object,), dict(hello=lambda self : print('hello world')))
a = Hello()
a.hello()
结果:
hello world
除了使用type创建类之外,还可以使用元类metaclass来创建。metaclass实际上还是使用type去创建一个新的类。
class myMetaClass(type):
def __new__(cls, name, bases, attrs):
attrs['test'] = lambda self, value: print(value)
return type.__new__(cls, name, bases, attrs)
class myClass(metaclass=myMetaClass):
pass
def main():
a = myClass()
a.test([1,2])
结果:
[1, 2]
由于python是动态语音,python里面任何错误都是通过异常来体现的,包括运行时的错误,“编译错误“等。
python所有的异常都是从BaseException类继承的,但是有一些是系统退出,用户键盘终端等与程序运行无关的异常,也是BaseException继承的。为了让用户可以更准确的捕获python的运行异常,通常只需要捕获Exception异常即可。我们自定义异常时,一般也是继承Exception类
一个异常的例子:
else分支是try没有捕获到异常执行,finally分支是最终都要执行的
class MyException(Exception):
def __str__(self):
return 'my Exception'
def main():
try:
raise MyException
except Exception as e:
print (e)
else:
print('else')
finally:
print('finally')
if __name__ == '__main__':
main()
结果:
my Exception
finally
异常如果没有被捕获,会一直向上抛出,最终python系统会打印一个错误的调用栈。
python中的断言也是一种异常,可以看做是一种条件异常,使用assert去判断用户的输入,而不要使用assert去判断系统的错误,系统的错误由异常来处理。
assert False, 'hello'
python的logging模块分为debug,info,warning,error等几个级别。log级别的顺序也是按这个顺序从低到高。如果开启了某一个级别的log,这个级别及以上的log都会显示。例如开启了debug级别,所有级别的log都会显示。如果开启了warning级别,warning,error级别的会显示。默认是warning级别。
import logging
logging.basicConfig(level=logging.INFO)
def main():
logging.info('info level')
logging.warning('warning level')
logging.debug('debug level')
logging.error('error level')
if __name__ == '__main__':
main()
结果:
INFO:root:info level
WARNING:root:warning level
ERROR:root:error level
使用pdb可以单步调试,使用下面的命令可以使用pdb调试
python3 -m pdb helloworld.py
也可以在代码中,使用下面代码加入断点
import pdb
pdb.set_trace()
详细的pdb使用参考python官方手册pdb部分
单元测试使用unittest模块,首先编写一个测试类,继承自unittest.TestCase,然后编写各种测试方法。setUp方法可以在每个测试例开始时执行,tearDown可以在每个测试例结束的时候执行。
下面是一个单元测试的例子
import unittest
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def get_grade(self):
if self.score >= 60 and self.score < 80:
return 'B'
elif self.score >= 80 and self.score <= 100:
return 'A'
elif self.score >=0 and self.score < 60:
return 'C'
else:
raise ValueError
class TestStudent(unittest.TestCase):
def setUp(self):
print('set up')
def tearDown(self):
print('tear down')
def test_80_to_100(self):
print('start test_80_to_100')
s1 = Student('Bart', 80)
s2 = Student('Lisa', 100)
self.assertEqual(s1.get_grade(), 'A')
self.assertEqual(s2.get_grade(), 'A')
print('end test_80_to_100')
def test_60_to_80(self):
print('start test_60_to_80')
s1 = Student('Bart', 60)
s2 = Student('Lisa', 79)
self.assertEqual(s1.get_grade(), 'B')
self.assertEqual(s2.get_grade(), 'B')
print('end test_60_to_80')
def test_0_to_60(self):
print('start test_0_to_60')
s1 = Student('Bart', 0)
s2 = Student('Lisa', 59)
self.assertEqual(s1.get_grade(), 'C')
self.assertEqual(s2.get_grade(), 'C')
print('end test_0_to_60')
def test_invalid(self):
print('start test_invalid')
s1 = Student('Bart', -1)
s2 = Student('Lisa', 101)
with self.assertRaises(ValueError):
s1.get_grade()
with self.assertRaises(ValueError):
s2.get_grade()
print('end test_invalid')
def main():
unittest.main()
if __name__ == '__main__':
main()
结果:
set up
start test_0_to_60
end test_0_to_60
tear down
.set up
start test_60_to_80
end test_60_to_80
tear down
.set up
start test_80_to_100
end test_80_to_100
tear down
.set up
start test_invalid
end test_invalid
tear down
.
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK
执行测试例有两个方法:一个是使用下面的代码执行
python3 -m unittest helloworld
还有一种是像上面的测试代码一样,在main函数里面执行unittest.main()。这种方式可以像执行普通文件一样,直接执行:
python3 helloworld.py
但是此方法在我的vscode中不能直接运行,需要从命令行输入上面的命令去运行。
python的文件操作接口和posix接口类似,打开文本文件时,还可以通过encoding参数选择编码,也可以通过errors参数忽略编码的错误,文件操作的示例:
def main():
with open('test.txt', 'w', encoding='gbk') as f:
f.write('测试gbk\r\n')
f.write('hello world\r\n')
f.write('哈哈哈\r\n')
with open('test.txt', 'r', errors='ignore') as f:
for line in f.readlines():
print(line, end='')
输出:
由于python3默认是utf8打开的,所以这里使用gbk编码的文件打开会出现错误,忽略编码错误之后,中文编码部分没有被读取出来
gbk
hello world
使用正确的编码测试:
def main():
with open('test.txt', 'w', encoding='gbk') as f:
f.write('测试gbk\r\n')
f.write('hello world\r\n')
f.write('哈哈哈\r\n')
with open('test.txt', 'r', encoding='gbk') as f:
for line in f.readlines():
print(line, end='')
输出:
测试gbk
hello world
哈哈哈
IO操作的函数不仅可以用在文件上面,也可以用在很多IO对象上面。StringIO和BytesIO可以创建IO对象。
from io import StringIO
from io import BytesIO
def main():
f = StringIO('test\r\nhello world\r\n哈哈哈\r\n')
for i in f:
print(i, end='')
print('-------------------')
f = StringIO()
f.write('new\r\n')
f.write('test\r\n')
print(f.getvalue())
print('-------------------')
f = BytesIO('测试'.encode('utf-8'))
for i in f:
print(i)
输出:
test
hello world
哈哈哈
-------------------
new
test
-------------------
b'\xe6\xb5\x8b\xe8\xaf\x95'
python的os模块包含了一些操作系统相关的操作。
获取环境变量
>>> os.environ.get('PATH')
'/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/sbin:/usr/sbin'
获取当前目录
>>> os.path.abspath('.')
'/media/bingfengfeifei/数据/PyCode'
连接目录,因为不同操作系统的目录分隔符不一样,所以需要使用python的目录连接来保证各个操作系统都可以生效
>>> a = os.path.join(os.path.abspath('.'),'new')
>>> os.mkdir(a)
>>> os.listdir('.')
['.vscode', 'helloworld.py', 'new', 'test.py', 'test.txt', '__pycache__']
>>> os.rmdir(a)
>>> os.listdir('.')
['.vscode', 'helloworld.py', 'test.py', 'test.txt', '__pycache__']
>>>
分割目录
>>> os.path.split('/media/bingfengfeifei/数据/PyCode/helloworld.py')
('/media/bingfengfeifei/数据/PyCode', 'helloworld.py')
分割扩展名
>>> os.path.splitext('/media/bingfengfeifei/数据/PyCode/helloworld.py')
('/media/bingfengfeifei/数据/PyCode/helloworld', '.py')
重命名
>>> os.mkdir('new')
>>> os.listdir('.')
['.vscode', 'helloworld.py', 'new', 'test.py', 'test.txt', '__pycache__']
>>> os.rename('new', 'newnew')
>>> os.listdir('.')
['.vscode', 'helloworld.py', 'newnew', 'test.py', 'test.txt', '__pycache__']
删除文件
>>> os.listdir('.')
['.vscode', 'helloworld.py', 'newnew', 'test.py', 'test.txt', '__pycache__']
>>> os.remove('test.txt')
>>> os.listdir('.')
['.vscode', 'helloworld.py', 'newnew', 'test.py', '__pycache__']
>>>
shutil模块还补充了一些os模块里面没有的系统操作。例如复制文件
>>> shutil.copyfile('helloworld.py', 'new.py')
pickle可以将内存中的数据结构,序列化成一个byte array,也可以将一个序列化好的byte array转换成一个对象。
import pickle
def main():
d = {'name':'蛤蛤',
'age':-1,
'test':[1,2,3,4]}
with open('dump.dat', 'wb') as f:
pickle.dump(d, f)
with open('dump.dat', 'rb') as f:
newDict = pickle.load(f)
print(newDict)
if __name__ == '__main__':
main()
输出:
{'name': '蛤蛤', 'age': -1, 'test': [1, 2, 3, 4]}
dump和load方法是将序列化好的东西向IO对象操作。dumps和loads操作是向byte array操作:
import pickle
def main():
d = {'name':'蛤蛤',
'age':-1,
'test':[1,2,3,4]}
s = pickle.dumps(d)
print(s)
newDict = pickle.loads(s)
print(newDict)
if __name__ == '__main__':
main()
输出:
b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x06\x00\x00\x00\xe8\x9b\xa4\xe8\x9b\xa4q\x02X\x03\x00\x00\x00ageq\x03J\xff\xff\xff\xffX\x04\x00\x00\x00testq\x04]q\x05(K\x01K\x02K\x03K\x04eu.'
{'name': '蛤蛤', 'age': -1, 'test': [1, 2, 3, 4]}
json模块可以将python中的数据结构和json结构来进行转换。同样可以使用dumps和dump两种方法,和pickle类似
import json
def main():
d = {'name':'蛤蛤',
'age':-1,
'test':[1,2,3,4],
'job':None,
'alive':True}
a = json.dumps(d)
print(a)
newDict = json.loads(a)
print(newDict)
if __name__ == '__main__':
main()
输出:
{"name": "\u86e4\u86e4", "age": -1, "test": [1, 2, 3, 4], "job": null, "alive": true}
{'name': '蛤蛤', 'age': -1, 'test': [1, 2, 3, 4], 'job': None, 'alive': True}
json还可以用来序列化class,需要自定义一个dumps中的default函数,用来从class转换为字典,还需要自定义一个loads里面的object_hook函数,用来从字典转换成对象。
class JsonTest:
def __init__(self, name = '蛤蛤', age=-1, test=[1,2,3,4], job=None, alive=True):
self.name = name
self.age = age
self.test = test
self.job = job
self.alive = alive
import json
def main():
d = JsonTest()
a = json.dumps(d,default=lambda x : x.__dict__)
print(a)
newDict = json.loads(a, object_hook=lambda d : JsonTest(d['name'], d['age'] + 1, d['test'], d['job'], d['alive']))
print(newDict.__dict__)
输出:
{"name": "\u86e4\u86e4", "age": -1, "test": [1, 2, 3, 4], "job": null, "alive": true}
{'name': '蛤蛤', 'age': 0, 'test': [1, 2, 3, 4], 'job': None, 'alive': True}
详细内容参考
廖雪峰python教程-多进程
在×Nix系统下面可以使用fork来创建子进程,Windows下面没有这个系统调用。
也可以使用multiprocessing.Process来在各个平台创建子进程,这个更为通用
如果创建的进程数量过多可以使用multiprocessing.Pool进程池来创建多个进程。
可以使用subprocess模块来启动一个子进程
import subprocess
def main():
print('$ ping www.python.org')
r = subprocess.call(['ping', 'www.python.org', '-c', '5'])
print('Exit code:', r)
结果:
$ ping www.python.org
PING dualstack.python.map.fastly.net (151.101.192.223) 56(84) bytes of data.
64 bytes from 151.101.192.223 (151.101.192.223): icmp_seq=1 ttl=51 time=234 ms
64 bytes from 151.101.192.223 (151.101.192.223): icmp_seq=2 ttl=51 time=238 ms
64 bytes from 151.101.192.223 (151.101.192.223): icmp_seq=3 ttl=51 time=231 ms
64 bytes from 151.101.192.223 (151.101.192.223): icmp_seq=4 ttl=51 time=229 ms
64 bytes from 151.101.192.223 (151.101.192.223): icmp_seq=5 ttl=51 time=225 ms
--- dualstack.python.map.fastly.net ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4003ms
rtt min/avg/max/mdev = 225.711/231.863/238.158/4.302 ms
Exit code: 0
如果子进程是需要输入内容的,还可以使用communicate()方法来输入内容
import subprocess
def main():
print('$ import this')
p = subprocess.Popen(['python3'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, err = p.communicate('import this'.encode('utf-8'))
print(output.decode('utf-8'))
print('Exit code:', p.returncode)
if __name__ == '__main__':
main()
输出:
$ import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Exit code: 0
使用Queue来通信,实现一个生产者消费者模型:
from multiprocessing import Process, Queue
def producer(q):
print('process producer pid({0})'.format(os.getpid()))
for i in ['A', 'B', 'C', 'D', 'E']:
print('put value {0}'.format(i))
q.put(i)
time.sleep(random.random() * 3)
def consumer(q):
print('process consumer pid({0})'.format(os.getpid()))
while True:
print('start value')
value = q.get(True)
print('get value {0}'.format(value))
def main():
q = Queue()
pro = Process(target=producer, args=(q,))
con = Process(target=consumer, args=(q,))
pro.start()
con.start()
pro.join()
con.terminate()
输出:
process producer pid(16750)
put value A
process consumer pid(16751)
start value
get value A
start value
put value B
get value B
start value
put value C
get value C
start value
put value D
get value D
start value
put value E
get value E
start value
python中的线程使用threading模块
import threading
import time
def loop():
print('thread {0} is starting.'.format(threading.current_thread().name))
for i in range(5):
print('thread {0} loop {1}.'.format(threading.current_thread().name, i))
time.sleep(1)
print('thread {0} end.'.format(threading.current_thread().name))
def main():
t = threading.Thread(target=loop,name='test')
t2 = threading.Thread(target=loop,name='test2')
t.start()
t2.start()
t.join()
t2.join()
输出:
thread test is starting.
thread test2 is starting.
thread test loop 0.
thread test2 loop 0.
thread test loop 1.
thread test2 loop 1.
thread test2 loop 2.
thread test loop 2.
thread test loop 3.
thread test2 loop 3.
thread test loop 4.
thread test2 loop 4.
thread test end.
thread test2 end.
python中的锁,使用threading.Lock()创建,acquire方法来获取锁,release方法来释放锁
lock = threading.Lock()
def loop():
lock.acquire()
try:
print('thread {0} is starting.'.format(threading.current_thread().name))
for i in range(5):
print('thread {0} loop {1}.'.format(threading.current_thread().name, i))
time.sleep(1)
print('thread {0} end.'.format(threading.current_thread().name))
finally:
lock.release()
CPython解释器有一个GIL锁(Global Interpreter Lock),每个python线程执行前都要先获取GIL,然后每执行100条指令释放锁(python3版本改变成了每隔一段时间释放锁),以便于其他线程执行。所以CPython解释器的线程并不能实现真正的并发。
每个线程里面可以共享全局变量,使用线程ThreadLocal来实现,使用threading.local()来创建ThreadLocal对象
import threading
import time
thread_local = threading.local()
def loop():
thread_local.num = [x for x in range(10)]
start()
def start():
a = thread_local.num
for i in a:
print('thread {0} : {1}'.format(threading.current_thread().name, i))
def main():
t = []
for i in range(multiprocessing.cpu_count()):
thread = threading.Thread(target=loop)
t.append(thread)
thread.start()
for i in t:
i.join()
if __name__ == '__main__':
main()
贪婪模式:如果一个正则表达式可以匹配出多个结果,他会尽量多的匹配字符
非贪婪模式:尽量少的匹配字符
表达式 | 作用 |
---|---|
. | 匹配任意字符除了换行符 |
^ | 匹配字符串的开头 |
$ | 匹配字符串的结尾 |
+ | 表示至少一个字符 |
* | 表示任意个字符 |
? | 表示一个或0个字符 |
*?, +?, ?? | 正则表达式默认是贪婪匹配,在匹配符后面加一个问号开启非贪婪的模式 |
{m} | 匹配确定m个字符 |
{m,n} | 匹配m到n个字符 |
{m,n}? | 匹配m到n个字符的非贪婪模式 |
\ | 转义字符 |
[] | 匹配中括号中执行的字符,例如[a-z],[0-9A-Z] |
[^5] | 匹配除了5的所有字符 |
A|B | 匹配A或者B |
(…) | 匹配括号里面的表达式,并且创建一个group |
\number | 匹配第number个group的内容 (\d{3})A\1可以匹配’123A123’ |
\A | 只在字符串的开头匹配,需要配合其他表达式使用 |
\b | 匹配单词的开头或者结尾,需要配合其他匹配使用 |
\B | 匹配非单词的开头或者结尾与\b相对 |
\d | 匹配数字 |
\D | 匹配非数字 |
\s | 匹配空白符 |
\S | 匹配非空白符 |
\w | 匹配字母数字下划线 |
\W | 匹配字母数字下划线之外的字符 |
\Z | 只在字符串的尾部匹配,需要配合其他表达式使用 |
s = r'123 A1 2 3fd s a f daA123A321Af'
a = re.split('\s+', s)
print(a)
输出:
['123', 'A1', '2', '3fd', 's', 'a', 'f', 'daA123A321Af']
s = r'123 A1 2 3fd s a f daA123A321Af'
a = re.match(r'(\A.)(\d{2}).+([a-zA-Z]{2}).+(.\Z)', s)
print(a.groups())
输出:
('1', '23', 'aA', 'f')
s1 = r'[email protected]'
s2 = r'[email protected]'
pattern = re.compile(r'(.+)@(.+)')
print(pattern.match(s1).groups())
print(pattern.match(s2).groups())
输出:
('xszhengdelun', '126.com')
('sa614374', 'mail.ustc.edu.cn')
from datetime import datetime
def main():
print(datetime.now())
dt = datetime(2017, 3, 16, 0, 0)
print(dt)
print(datetime.now().timestamp())
print(datetime.fromtimestamp(1429417200.0))
print(datetime.strptime('2018-10-05 18:55:22', '%Y-%m-%d %H:%M:%S'))
print(datetime.now().strftime('%a, %b %d %H:%M'))
if __name__ == '__main__':
main()
输出:
2018-10-07 21:41:19.897650
2017-03-16 00:00:00
1538919679.897687
2015-04-19 12:20:00
2018-10-05 18:55:22
Sun, Oct 07 21:41
from collections import namedtuple
from collections import deque
from collections import defaultdict
from collections import OrderedDict
from collections import Counter
def main():
Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2)
print('p =',p)
deq = deque(['a', 'b', 'c'])
deq.append('d')
deq.appendleft('z')
print('deq =',deq)
deq.pop()
deq.popleft()
print('deq =',deq)
dd = defaultdict(lambda : 'N/A')
dd['A'] = 1
dd['B'] = 2
print(dd['C'])
od = OrderedDict({'a':1, 'b':2, 'c':3}) #有序dict
print('od =',od)
c = Counter()
for i in 'hello world!':
c[i] += 1
print('c = ', c)
if __name__ == '__main__':
main()
输出:
p = Point(x=1, y=2)
deq = deque(['z', 'a', 'b', 'c', 'd'])
deq = deque(['a', 'b', 'c'])
N/A
od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
c = Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1, '!': 1})
base64是一种二进制编码方式,采用64个字符来表示任意的二进制数据
import base64
def main():
a = base64.b64encode('冰封飞飞'.encode('utf-8'))
print(a)
print(base64.b64decode(a).decode('utf-8'))
a = base64.urlsafe_b64encode(b'test-url-safe')
print(a)
print(base64.urlsafe_b64decode(a))
if __name__ == '__main__':
main()
输出:
b'5Yaw5bCB6aOe6aOe'
冰封飞飞
b'dGVzdC11cmwtc2FmZQ=='
b'test-url-safe'
struct模块可以处理字节相关的数据。详细内容参考python官方手册struct
字符 | 字节序 | 尺寸 | 对齐 |
---|---|---|---|
< | 小端序 | 标准 | 无 |
> | 大端序 | 标准 | 无 |
! | 网络序 | 标准 | 无 |
@ | 主机序 | native | native |
= | 主机序 | 标准 | 无 |
格式 | C语言类型 | Python 类型 | 标准尺寸 |
---|---|---|---|
x | 填充类型 | no value | |
c | char | 长度为1的bytes | 1 |
b | signed char | integer | 1 |
B | unsigned char | integer | 1 |
? | _Bool | bool | 1 |
h | short | integer | 2 |
H | unsigned short | integer | 2 |
i | int | integer | 4 |
I | unsigned int | integer | 4 |
l | long | integer | 4 |
L | unsigned long | integer | 4 |
q | long long | integer | 8 |
Q | unsigned long long | integer | 8 |
n | ssize_t | integer | |
N | size_t | integer | |
e | float | 2 | |
f | float | float | 4 |
d | double | float | 8 |
s | char[] | bytes | |
p | char[] | bytes | |
P | void* | integer |
import struct
def main():
a = struct.pack('=hHQ',-100, 100,100000000000)
print(a.hex())
if __name__ == '__main__':
main()
输出:
9cff640000e8764817000000
import hashlib
def main():
s = '测试MD5哈希摘要算法'
md5 = hashlib.md5()
md5B = hashlib.md5()
md5.update(s.encode('utf-8'))
for i in s:
md5B.update(i.encode('utf-8'))
print(md5.hexdigest())
print(md5B.hexdigest())
sha256 = hashlib.sha256()
sha256B = hashlib.sha256()
sha256.update(s.encode('utf-8'))
for i in s:
sha256B.update(i.encode('utf-8'))
print(sha256.hexdigest())
print(sha256B.hexdigest())
输出:
725b5fca27c243ab4e61b02b0b74b6e2
725b5fca27c243ab4e61b02b0b74b6e2
bc8497b01d5d7d37bda3e8f39c291b73127242709e761fa38f0a7978f72e3fdc
bc8497b01d5d7d37bda3e8f39c291b73127242709e761fa38f0a7978f72e3fdc
import hmac
def main():
a = hmac.new(b'test', '测试HMAC'.encode('utf-8'), digestmod='sha256')
print(a.hexdigest())
输出:
4599b96ad2c95e565f4f8d29c925a3eb0d3549b0b913d996b18393a02a03ea42
import itertools
def main():
a = itertools.count(1)
for i in a:
print(i) # 1,2,3,4,5,6,7...无限打印
a = itertools.cycle('ABCD')
for i in a:
print(i) # A B C D A B C D 循环无限打印
a = itertools.repeat('A', 3)
for i in a:
print(i) # A A A 打印3次
a = itertools.count(1)
b = itertools.takewhile(lambda x : x <= 10, a)
for i in b:
print(i) #打印 1 2 3 4 5 6 7 8 9 10
for i in itertools.chain('ABC', 'XYZ'):
print(i) #打印 A B C X Y Z 连接多个迭代对象
groupby将相邻的重复元素挑选出来放在一组
import itertools
def main():
a = itertools.groupby('AAabBbBcCAaa', lambda c : c.upper())
for k, group in a:
print(k, list(group))
输出:
A ['A', 'A', 'a']
B ['b', 'B', 'b', 'B']
C ['c', 'C']
A ['A', 'a', 'a']
使用with可以让open语句在离开with时,自动调用close
with open('test.txt', 'r') as f:
f.read()
我们可以通过重载__enter__和__exit__来实现上下文管理。
class myContext:
def __enter__(self):
print('enter')
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
print('Error')
else:
print('End')
def main():
with myContext() as f:
pass
输出:
enter
End
也可以使用@contextmanager装饰器来定义
from contextlib import contextmanager
class myContext:
pass
@contextmanager
def context_func():
print('begin')
f = myContext()
yield f
print('end')
def main():
with context_func() as f:
pass
if __name__ == '__main__':
main()
输出:
begin
end
还可以使用@contextmanager实现在调用前后自动执行语句
@contextmanager
def tag(name):
print("<%s>" % name)
yield
print("%s>" % name)
with tag("h1"):
print("hello")
print("world")
输出:
hello
world
使用closing函数可以将任意对象转换为可以在with里面使用的对象。closing定义如下,实际上就是在执行完之后调用close方法。如果转换的对象没有close方法,就GG了。
@contextmanager
def closing(thing):
try:
yield thing
finally:
thing.close()
用于操作URL
from xml.parsers.expat import ParserCreate
from html.parser import HTMLParser
PIL是python2的图形处理库,Pillow是将PIL适配到python3的版本
requests库是处理URL的第三方库,使用起来比urllib要简单
chardet可以预测bytes的编码,应用在TCP章节
获取系统信息的工具,可以获取CPU,内存,磁盘,网络,进程
展示命令行进度条
virtualenv可以隔离python运行环境,解决不同版本python之间的冲突问题
python的socket使用思路和posix接口类似,只不过参数的传入要简单,因为没有类型的概念,所以记忆量要小很多,基本可以直接裸写。linux下C语言的socket裸写压力还有有点大。
Server代码:
# -*- coding: utf-8 -*-
import socket
import chardet
import threading
def handlePacket(sock, addr):
print(sock)
print(addr)
while True:
data = sock.recv(1024)
encoding = chardet.detect(data)['encoding']
data = data.decode(encoding)
print(data)
if not data or data[:4] == 'exit':
break
data = 'Hello :' + data
sock.send(data.encode(encoding))
sock.close()
def main():
with socket.socket() as s:
s.bind(('localhost', 6666))
s.listen()
while True:
sock, addr = s.accept()
handleThread = threading.Thread(target=handlePacket,args=(sock, addr))
handleThread.start()
if __name__ == '__main__':
main()
Client代码:
# -*- coding: utf-8 -*-
import time
import socket
def main():
buffer = []
with socket.socket() as s:
data = ('MyClient', 'Test', "Python", 'exit', 'after')
s.connect(('localhost', 6666))
for i in data:
s.send(i.encode('utf-8'))
recvData = s.recv(1024)
print(recvData.decode('utf-8'))
if __name__ == '__main__':
main()
Client也可以直接使用nc来连接
bingfengfeifei@bingfengfeifei-PC:nc localhost 6666
udp的代码写了一个客户端和服务器ping pang报文的测试
Server:
# -*- coding: utf-8 -*-
import socket
import chardet
import threading
def main():
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.bind(('localhost', 6666))
while True:
data, addr = s.recvfrom(1024)
print(data)
s.sendto(data, addr)
if __name__ == '__main__':
main()
Client:
# -*- coding: utf-8 -*-
import time
import socket
def main():
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.sendto(b'ping pang test', ('localhost', 6666))
while True:
data, addr = s.recvfrom(1024)
print(data,i)
s.sendto(data, addr)
if __name__ == '__main__':
main()
debian系mysql安装,安装过程中会输入密码,用5.7有问题,换成5.6版本了。
sudo apt install mysql-server-5.6
mysql -u root -p #root账户进入数据库
create database test #创建数据库
修改mysql配置文件vi /etc/mysql/my.cnf,添加如下内容
[client]
default-character-set = utf8
[mysqld]
default-storage-engine = INNODB
character-set-server = utf8
collation-server = utf8_general_ci
进入数据库输入下面内容,查看是否生效
show variables like '%char%';
pip安装mysql驱动,两者选其一
pip3 install mysql-connector-python --allow-external mysql-connector-python
pip3 install mysql-connector
下面创建表的时候要写上charset=utf8,否则不支持中文
# -*- coding: utf-8 -*-
import time
import socket
import sqlite3
import os
import mysql.connector
def main():
conn = mysql.connector.connect(user='root', password='123456', database='test', charset='utf8')
cursor = conn.cursor()
try:
cursor.execute('drop table user')
except Exception:
pass
try:
cursor.execute('create table user (id varchar(20) primary key , name varchar(20)) charset=utf8')
cursor.execute('insert into user (id, name) values (%s, %s)', ['1', 'afbcd冰封飞飞'])
print(cursor.rowcount)
conn.commit()
cursor.close()
cursor = conn.cursor()
cursor.execute('select * from user where id = %s' ,('1',))
values = cursor.fetchall()
print(values)
except Exception as e:
print(e)
finally:
pass
if __name__ == '__main__':
main()
输出:
1
[('1', 'afbcd冰封飞飞')]
使用ORM框架的话可以尝试SQLAlchemy,安装方式:
pip3 install sqlalchemy
协程是一种轻量级的线程,在一些编程语言中语法支持,python中可以通过yeild来实现协程。C和C++目前在语法层面不支持协程,需要通过一些底层的手段hack实现, 在说协程之前需要先了解几个概念。
并发强调的是在一个时间段,有多个任务可以执行,但是这里不要求多个任务同时执行,他们可以交替执行,类似于单核处理器,操作系统的进程调度。多个任务交替执行,在用户的眼里是同时运行的。
并行强调的是多个任务同时执行,而不是并发这种可以交替执行的行为。例如,在多核处理器上面,不同任务可以在不同的核上在同一时刻同时执行。
多个线程可能跑在同一个CPU核上,也可以跑在多个CPU核上,如果跑在一个CPU核上的话,多个线程就是由调度器来调度交替执行。如果绑在了不同的CPU核上的话,可以认为两个线程可以并行的执行。但是无论线程以那种形式运行,从宏观上来看,线程是“同时运行”,而且如果没有线程同步的话,多个线程之间的执行顺序是随机的,而且线程之间可能存在资源争用,访问全局资源时,通常需要加锁。
协程也是多个任务交替执行,但是协程的控制流程,函数的切换都是自己代码实现的,协程是多个任务协作完成的,但是逻辑上是连贯的,协程可以让多个任务按照你的逻辑顺序来执行,保证思维的连贯性,这种逻辑的顺序性也让协程不需要考虑全局资源争用的问题,也就不需要加锁。和线程相比,这种主动让出型调度比线程的被动调度要高效,也比操作系统的调度代价要小得多。
使用多线程的生产者消费者模式,如果没有同步的话,生成者和消费者之间执行的次序会随机执行,由调度决定,而协程版本,可以由调用者来决定执行顺序,在这里我们使生产者和消费者交替执行。
def consumer():
while True:
print('waiting data from producer...')
data = yield
print('data = {0}'.format(data))
def producer(con):
data = range(1, 20)
con.send(None)
for i in data:
print('generator data {0}'.format(i))
con.send(i)
con.close()
def main():
con = consumer()
producer(con)
输出:
waiting data from producer...
generator data 1
data = 1
waiting data from producer...
generator data 2
data = 2
waiting data from producer...
generator data 3
data = 3
waiting data from producer...
generator data 4
data = 4
waiting data from producer...
generator data 5
data = 5
waiting data from producer...
generator data 6
data = 6
waiting data from producer...
generator data 7
data = 7
waiting data from producer...
generator data 8
data = 8
waiting data from producer...
generator data 9
data = 9
waiting data from producer...
generator data 10
data = 10
waiting data from producer...
generator data 11
data = 11
waiting data from producer...
generator data 12
data = 12
waiting data from producer...
generator data 13
data = 13
waiting data from producer...
generator data 14
data = 14
waiting data from producer...
generator data 15
data = 15
waiting data from producer...
generator data 16
data = 16
waiting data from producer...
generator data 17
data = 17
waiting data from producer...
generator data 18
data = 18
waiting data from producer...
generator data 19
data = 19
waiting data from producer...
yeild from是新增的语法,PEP380讨论加入,用法:RESULT = yield from EXPR
yeild from后面跟一个生成器对象,遍历生成器,将生成器yeild的每一个值返回给调用方。这样可以将嵌套的协程简化编写。
在一个生成器函数gen中执行yeild from subgen()时,subgen会获得控制权,把产出的值传给gen的调用方,即调用方可以直接控制嵌套的subgen()。与此同时,gen会阻塞,直到subgen终止执行。
下面看一个yeild from的例子
def subgen():
for i in range(5):
r = yield i
print('sub gen recive send{0}'.format(r))
return 'return mywait'
def gen():
for i in range(2):
print("hello world!")
r = yield from subgen()
print(r)
print('hello again!')
def main():
try:
g = gen()
next(g)
while True:
print(g.send('A'))
except Exception as e:
pass
输出:
hello world!
sub gen recive sendA
1
sub gen recive sendA
2
sub gen recive sendA
3
sub gen recive sendA
4
sub gen recive sendA
return mywait
hello again!
hello world!
0
sub gen recive sendA
1
sub gen recive sendA
2
sub gen recive sendA
3
sub gen recive sendA
4
sub gen recive sendA
return mywait
hello again!
在这个例子中,r = yield from subgen()
,执行到这句话时,函数的控制权从gen()转移到subgen()中,并且遍历执行。这里yeild出来5个值0,1,2,3,4,通过yeild from直接传递给调用方main函数。所以在print(g.send('A'))
时,就打印出来了subgen() yeild的值,而在main函数send的数值,也可以直接传递给subgen()里面中去,yeild from像是一个通道打通了subgen()和main()之间的数据,gen()和subgen()之间的数据交换。r = yield from subgen()
这句话的r的作用是可以接受subgen()的返回值, gen()也可以通过这样的写法来获取到subgen()给自己传递的信息。
以上的协程只是展示了一个在单线程内,任务切换的功能。如何实现真正的异步,还需要配合消息循环和事件来处理。asyncio是python3.4新引入的标准库,内置了异步的IO。asyncio的编程模型是获取模块的主消息循环,并且把协程扔到消息循环中,实现异步IO。这样可以在协程执行耗时操作的IO操作时,交出控制权,调度器来执行其他的协程,当IO操作执行完毕时,又可以切回执行完IO操作的协程。
asyncio包使用的协程是较为严格的定义,适合asyncio API的协程在定义中必须使用yield from,而不能使用yeild。@asyncio.coroutine装饰器应该使用到协程上面,并不是强求的,但是强烈建议这样做。可以将协程从普通函数中凸出出来。
下面一个例子,使用协程来实现异步操作,程序的效果是模拟执行一个耗时的操作,然后控制台绘制一个光标旋转的操作,来表示有任务正在执行。
# -*- coding: utf-8 -*-
import asyncio
import itertools
import sys
@asyncio.coroutine
def spin(msg):
write, flush = sys.stdout.write, sys.stdout.flush
for char in itertools.cycle('|/-\\'):
status = char + ' ' + msg
write(status)
flush()
write('\x08' * len(status))
try:
yield from asyncio.sleep(.1)
except asyncio.CancelledError:
break
write(' ' * len(status) + '\x08' * len(status))
@asyncio.coroutine
def slow_function():
yield from asyncio.sleep(3)
return 42
@asyncio.coroutine
def supervisor():
spinner = asyncio.ensure_future(spin('thinking'))
print('spinner object:', spinner)
result = yield from slow_function()
spinner.cancel()
return result
def main():
loop = asyncio.get_event_loop()
result = loop.run_until_complete(supervisor())
loop.close()
print('Answer:', result)
if __name__ == '__main__':
main()
该程序的main()函数,将协程supervisor()加入主事件循环。
在supervisor()中,使用asyncio.ensure_future创建了一个协程任务(在python3.7+的版本,应该使用asyncio.create_task来创建。)然后使用yield from slow_function()来模拟执行一个耗时的操作。之后耗时操作执行完毕,关闭spinner协程。
slow_function()里面使用asyncio.sleep(3)来模拟一个耗时3秒的操作,对于slow_function()来说,这里会阻塞3秒的时间,而对于整个事件循环来说,这里会将控制权交给主循环,会继续执行其他的协程。这就是异步的sleep,如果使用time.sleep的话,整个主循环都会阻塞在这里,其他的协程不会被调度。
spin()函数里面循环打印字符来模拟等待。’\x08’是退格操作。
在这个例子中,slow_function()的耗时操作和spin()函数的打印是并发执行的,但是仅用到了一个线程。
python3.5使用了新的语法来表示协程,用于替代@asyncio.coroutine装饰器和yield from语法。如果使用python3.5的协程新语法的话,只需要将
@asyncio.coroutine替换为async
将yield from替换为await
使用新的语法可以更加清晰的表明协程,也和其他支持协程的语言的语法更加一致。
将上面的协程代码替换为新的语法:
# -*- coding: utf-8 -*-
import asyncio
import itertools
import sys
async def spin(msg):
write, flush = sys.stdout.write, sys.stdout.flush
for char in itertools.cycle('|/-\\'):
status = char + ' ' + msg
write(status)
flush()
write('\x08' * len(status))
try:
await asyncio.sleep(.1)
except asyncio.CancelledError:
break
write(' ' * len(status) + '\x08' * len(status))
async def slow_function():
await asyncio.sleep(3)
return 42
async def supervisor():
spinner = asyncio.ensure_future(spin('thinking'))
print('spinner object:', spinner)
result = await slow_function()
spinner.cancel()
return result
def main():
loop = asyncio.get_event_loop()
result = loop.run_until_complete(supervisor())
loop.close()
print('Answer:', result)
if __name__ == '__main__':
main()
aiohttp是一个异步的http框架,下面是使用aiohttp创建一个http server
# -*- coding: utf-8 -*-
import asyncio
from aiohttp import web
routes = web.RouteTableDef()
@routes.get('/')
async def index(request):
#await asyncio.sleep(0.5)
return web.Response(body=b'Index
', content_type='text/html')
@routes.get('/hello/{name}')
async def hello(request):
#await asyncio.sleep(0.5)
text = 'hello, %s!
' % request.match_info['name']
return web.Response(body=text.encode('utf-8'), content_type='text/html')
def init():
app = web.Application()
app.router.add_routes(routes)
web.run_app(app, host='localhost', port='8080')
init()
下面是一个aiohttp作为client的例子:
使用aiohttp,使用协程异步下载几个文件
# -*- coding: utf-8 -*-
import time
import sys
import os
import asyncio
import aiohttp
POP20_CC = ('CN IN US ID BR PK NG BD RU JP MX PH VN ET EG DE IR TR CD FR').split()
BASE_URL = 'http://flupy.org/data/flags'
DEST_DIR = 'downloads/'
def save_flag(img, filename):
path = os.path.join(DEST_DIR, filename)
with open(path, 'wb') as fp:
fp.write(img)
def get_flag(cc):
url = '{}/{cc}/{cc}.gif'.format(BASE_URL, cc=cc.lower())
resp = requests.get(url)
return resp.content
def show(text):
print(text, end=' ')
sys.stdout.flush()
async def get_flag(cc):
url = '{}/{cc}/{cc}.gif'.format(BASE_URL, cc=cc.lower())
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
image = await resp.read()
return image
async def download_one(cc):
image = await get_flag(cc)
show(cc)
save_flag(image, cc.lower() + '.gif')
return image
def download_many(cc_list):
loop = asyncio.get_event_loop()
to_do = [download_one(cc) for cc in sorted(cc_list)]
wait_coro = asyncio.wait(to_do)
res, _ = loop.run_until_complete(wait_coro)
loop.close()
return len(res)
def main(download_many):
t0 = time.time()
count = download_many(POP20_CC)
elapsed = time.time() - t0
msg = '\n{} flags downloaded in {:.2f}s'
print(msg.format(count, elapsed))
if __name__ == '__main__':
main(download_many)
future指一种对象,表示异步执行的结果,是concurrent.futures模块和asyncio包重要的组件,可是这两个库的用户,有时却看不到future。我们编写的代码可能没有直接使用future。
从python3.4起,标准库中有两个名为Future的类,concurrent.futures.Future和asyncio.Future。这两个类的作用相同,都表示可能已经完成或者尚未完成的延迟计算。
future封装待完成的操作,可以放入队列,完成的状态可以查询,得到结果后可以获取结果。
通常情况下,我们不应该自己创建future,而只能由并发框架(concurrent.futures或asyncio)实例化。
下面一个例子使用concurrent.futures模块模拟下载一些内容
# -*- coding: utf-8 -*-
from concurrent import futures
import time
import random
def download_one(cc):
time.sleep(random.random() * 2)
print('download {}'.format(cc))
return cc
def download_many(cc_list):
workers = min(20, len(cc_list))
with futures.ThreadPoolExecutor(workers) as executor:
res = executor.map(download_one, cc_list)
return len(list(res))
def main(download_many):
t0 = time.time()
count = download_many(['S1', 'S2', 'S3', 'S4', 'S5', 'S6'])
elapsed = time.time() - t0
msg = '\n{} flags downloaded in {:.2f}s'
print(msg.format(count, elapsed))
if __name__ == '__main__':
main(download_many)
输出:
download S1
download S4
download S3
download S5
download S2
download S6
6 flags downloaded in 1.91s
executor.map和内置map类似,不过download_one会在多个线程并发调用。和上面开头介绍的一样,这里我们并没有看到future,我们使用并发框架的时候一般不会自己去创建future。
下面的代码是我们手动创建future对象来实现
# -*- coding: utf-8 -*-
from concurrent import futures
import time
import random
def download_one(cc):
time.sleep(random.random() * 2)
print('download {}'.format(cc))
return cc
def download_many(cc_list):
cc_list = cc_list[:5]
with futures.ThreadPoolExecutor(max_workers=3) as executor:
to_do = []
for cc in sorted(cc_list):
future = executor.submit(download_one, cc)
to_do.append(future)
msg = 'Scheduled for {}: {}'
print(msg.format(cc, future))
results = []
for future in futures.as_completed(to_do):
res = future.result()
msg = '{} result: {!r}'
print(msg.format(future, res))
results.append(res)
return len(list(results))
def main(download_many):
t0 = time.time()
count = download_many(['S1', 'S2', 'S3', 'S4', 'S5', 'S6'])
elapsed = time.time() - t0
msg = '\n{} flags downloaded in {:.2f}s'
print(msg.format(count, elapsed))
if __name__ == '__main__':
main(download_many)
输出:
Scheduled for S1:
Scheduled for S2:
Scheduled for S3:
Scheduled for S4:
Scheduled for S5:
download S1
result: 'S1'
download S2
result: 'S2'
download S4
result: 'S4'
download S5
result: 'S5'
download S3
result: 'S3'
5 flags downloaded in 1.91s
executor.submit将任务提交对执行队列,然后返回一个future对象,这个时候对象就已经开始准备执行了。for future in futures.as_completed(to_do):
这个循环是等待执行完毕,获取结果。
由于受GIL限制,上面的代码都不能并行下载,但是GIL几乎对IO密集型的处理无害,标准库中的所有执行IO操作的函数,在等待操作系统返回结果时,都会释放GIL。这意味着在Python语言这个层次上可以使用多线程,而IO密集型的程序可以从中受益。上面的代码每个任务下载需要时间0-2秒,但是并发下载完5个对象,用时并没有5倍于单次下载的时间。
如果做CPU密集的操作,可以使用ProcessPollExecutor,这个模块将工作分配给多个Python进程处理,可以绕开GIL利用所有的CPU核心。使用方式只需要把上面的ThreadPoolExecutor替换成ProcessPoolExecutor即可
异步中的上下文管理器with和异步迭代器使用了新语法async with和async for。具体的简介参考https://blog.csdn.net/tinyzhao/article/details/52684473