python 学习
该复习内容多来自廖雪峰老师的教程同时也整合了一些其他博客的相关知识点。
之。前学习了一段时间,现在重温一些比较基础的语法,为以后的学习打好基础。
基本语法
循环
- for x in …循环就是把每个元素代入变量x,然后执行缩进块的语句。
>>> for x, y in [(1, 1), (2, 4), (3, 9)]: ... print(x, y) ... 1 1 2 4 3 9 for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方: >>> [x * x for x in range(1, 11) if x % 2 == 0] [4, 16, 36, 64, 100]
可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。第二种循环是while循环
区别可迭代对象和迭代器:
- 可迭代对象:可以直接作用于for循环的对象统称为可迭代对象:Iterable。
- 迭代器:可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator甚至可以表示一个无限大的数据流,例如全体自然数。
- 可迭代对象转变为迭代器:把list、dict、str等Iterable变成Iterator可以使用iter()函数:
切片操作
- 作用:指定索引范围的操作
取前3个元素
>>> L[0:3]['Michael', 'Sarah', 'Tracy']
L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引 0,1,2,正好是3个元素。
如果第一个索引是0,还可以省略:
>>> L[:3]['Michael', 'Sarah', 'Tracy']
前10个数,每两个取一个:
>>> L[:10:2][0, 2, 4, 6, 8]
所有数,每5个取一个:
>>> L[::5][0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
甚至什么都不写,只写[:]就可以原样复制一个list:
>>> L[:][0, 1, 2, 3, ..., 99]
tuple也是一种list,唯一区别是tuple不可变。因此,tuple也可以用切片操作,只是操作的结果仍是tuple:
>>> (0, 1, 2, 3, 4, 5)
[:3](0, 1, 2)
字符串'xxx'也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串:
>>> 'ABCDEFG'[:3]
'ABC'
>>> 'ABCDEFG'[::2]
'ACEG'
判断是否是字符串:
isinstance(x, str) #可替换为str,int,float,dict产生例外
raise语句允许程序员强行产生指定的例外。
>>> raise NameError, 'HiThere'
Traceback (innermost last):
File "", line 1
NameError: HiThere
raise语句的第一个参数指定要产生的例外的名字。可选的第二参数指定例外的参数。
else:
raise ValueError('bad score')
- id 函数
id(object)返回的是对象的“身份证号”,唯一且不变,但在不重合的生命周期里,可能会出现相同的id值。此处所说的对象应该特指复合类型的对象(如类、list等),对于字符串、整数等类型,变量的id是随值的改变而改变的。用is判断两个对象是否相等时,依据就是这个id值
输入输出
Python提供了一个input(),可以让用户输入字符串,并存放到一个变量里。name = input()
Python还允许用r”表示”内部的字符串默认不转义
用全部大写的变量名表示常量只是一个习惯上的用法
除法:
- / 除法计算结果是浮点数,即使是两个整数恰好整除,结果也是浮点数
- 还有一种除法是//,称为地板除,两个整数的除法仍然是整数
- Python的整数没有大小限制, Python的浮点数也没有大小限制,但是超出一定范围就直接表示为inf(无限大)。
# -- coding: utf-8 -- 告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。
编码:
Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符
Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。
- Python对bytes类型的数据用带b前缀的单引号或双引号表示:x = b’ABC’要注意区分’ABC’和b’ABC’,前者是str,后者虽然内容显示得和前者一样,但bytes的每个字符都只占用一个字节。
- 使用 ‘ABC’.encode(‘utf-8’)或者’ABC’.encode(‘ascii’)编码为指定的bytes。反过来可以使用b’ABC’.decode(‘utf-8’)或者b’ABC’.decode(‘ascii’)将bytes解码为str
- 在操作字符串时,我们经常遇到str和bytes的互相转换。为了避免乱码问题,应当始终坚持使用UTF-8编码对str和bytes进行转换。
在Python中,采用的格式化方式和C语言是一致的,用%实现,举例如下:’Hi, %s, you have 1000000.’ %运算符就是用来格式化字符串的。在字符串内部,%s表示用字符串替换,%d表示用整数替换,%f表示浮点数。有几个%?占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个%?,括号可以省略
其中,格式化整数和浮点数还可以指定是否补0和整数与小数的位数:’%.2f’ % 3.1415926’3.14’
格式化输出:方法 str.format() 的基本用法如下:
>>> print('{0} and {1}'.format('spam', 'eggs'))
spam and eggs
>>> print('{1} and {0}'.format('spam', 'eggs'))
eggs and spam
可以通过参数名来引用值:
>>> print('This {food} is {adjective}.'.format(
... food='spam', adjective='absolutely horrible'))
This spam is absolutely horrible.
字段名后允许可选的 ‘:’ 和格式指令。这允许对值的格式化加以更深入的控制。下例将 Pi 转为三位精度。
>>> import math
>>> print('The value of PI is approximately {0:.3f}.'.format(math.pi))
The value of PI is approximately 3.142.
在字段后的 ‘:’ 后面加一个整数会限定该字段的最小宽度,这在美化表格时很有用:
>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for name, phone in table.items():
... print('{0:10} ==> {1:10d}'.format(name, phone))
...
Jack ==> 4098
Dcab ==> 7678
Sjoerd ==> 4127
内置数据类型
列表list
- 定义:有序集合,可以随时添加和删除
- 赋值:
- classmates = [‘Michael’, ‘Bob’, ‘Tracy’]
- 要把某个元素替换成别的元素,可以直接赋值给对应的索引位置
- 二维数组:p = [‘asp’, ‘php’] s = [‘python’, ‘java’, p, ‘scheme’] 要拿到’php’可以写p[1]或者s[2][1]
- 添加:
- 从最后添加:classmates.append(‘Adam’)
- 指定位置添加:classmates.insert(1, ‘Jack’)
- 删除
- 删除末尾:classmates.pop()
- 删除指定位置的元素:classmates.pop(1)
- 方法函数:
- len(classmates) -》 3
- 如果要取最后一个元素,除了计算索引位置外,还可以用-1做索引,直接获取最后一个元素
元组tuple
- 定义:与list类似,一旦初始化就不能修改。tuple不可变,所以代码更安全。
- 赋值:
- classmates = (‘Michael’, ‘Bob’, ‘Tracy’)
- 只有1个元素的tuple定义时必须加一个逗号,,来消除歧义: t = (1,)
- 可变的tuple:
- t = (‘a’, ‘b’, [‘A’, ‘B’]) t[2][0] = ‘X’ t[2][1] = ‘Y’ print(t)
字典dict
- 定义:键值对
- 赋值:
- d = {‘Michael’: 95, ‘Bob’: 75, ‘Tracy’: 85} d[‘Michael’] 95
- 判断:判断key是否存在:’Thomas’ in d False
- 增加:d[‘afc’] = 100
- 删除:d.pop(key)
- 查找:
- d[‘Michael’] #直接数组访问
- d.get(‘Thomas’,-1) #如果不存在返回值
- 修改:d[‘Michael’] = 100 #直接数组修改
- 迭代遍历:
- 默认情况下,dict迭代的是key。
- 如果要迭代value,可以用for value in d.values()
- 如果要同时迭代key和value,可以用for k, v in d.items()。
集合set
- 定义:没有重复元素
- 赋值:需要提供一个list或tuple或dict(key部分)作为输入集合。如:s=set([1,2,3])
- 增加:
- add(key) #key作为整体添加到集合中
- update(key) #传入元素拆分成单个字符
- 删除:
- remove(key)
- discard(key)
- pop() #删除S中一个不确定元素
- clear() #清除所有元素
- 查找:
- x in s
- x not in s
- 交并补操作
range()函数,可以生成一个整数序列:
- 通过list()函数可以转换为list。比如range(5)生成的序列是从0开始小于5的整数: list(range(5)) [0, 1, 2, 3, 4] list(range(3,5))可以指定开始和结束
生成器:generator
- 定义:一边循环一边计算的机制,称为生成器。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:
- generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
generator函数的“调用”实际返回一个generator对象:
>>> f = fib(6) >>> f <generator object fib at 0x104feaaa0>
但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
def fib(max):
n, a, b = 0,0,1
while n < max:
yield b
a, b = b, a+b
n = n + 1
return 'done'
g = fib(6)
while True:
try:
x = g.next()
print('g',x)
except StopIteration as e:
print('return value:',e.value)
break
g 1
g 1
g 2
g 3
g 5
g 8
return value: done
close():关闭生成器。生成器被关闭后,再次调用next()方法,不管能否遇到yield关键字,都会立即抛出StopIteration异常。
- send():向生成器内部传递参数
def count(n): x = 0 while x < n: value = yield x if value is not None: print 'Received value: %s' %value x += 1 gen = count(5) print gen.next() # print 0 print gen.send('Hello') # Received value: Hello, then print 1
我们先调用next()方法,让代码执行到yield关键字(这步必须要),当前打印出0。然后当我们调用”gen.send(‘Hello’)”时,字符串’Hello’就被传入生成器中,并作为yield关键字的执行结果赋给变量”value”,所以控制台会打印出”Received value: Hello”。然后代码继续执行,直到下一次遇到yield关键字后暂定,此时生成器返回的是1。
简单的说,send()就是next()的功能,加上传值给yield。如果你有兴趣看下Python的源码,你会发现,其实next()的实现,就是send(None)。- throw():传递异常
- send():向生成器内部传递参数
def throw_gen():
try:
yield 'Normal'
except ValueError:
yield 'Error'
finally:
print 'Finally'
gen = throw_gen()
print gen.next() # Normal
print gen.next() # Finally, then StopIteration
gen = throw_gen()
print gen.next() # Normal
print gen.throw(ValueError) # Error
print gen.next() # Finally, then StopIteration
throw()方法向生成器函数内部传递了”ValueError”异常,代码进入”except ValueError”语句,当遇到下一个yield时才暂停并退出,此时生成器返回的是’Error’字符串。简单的说,throw()就是next()的功能,加上传异常给yield。
函数
定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。
可变参数:可变参数就是传入的参数个数是可变的。如:
在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数.
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
Python允许你在list或tuple前面加一个*号,把list或tuple的元素变成可变参数传进去:
nums = [1, 2, 3]
calc(*nums)
*nums表示把nums这个list的所有元素作为可变参数传进去。这种写法相当有用,而且很常见。
- 关键字参数
关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
>>> person('Michael', 30)
name: Michael age: 30 other: {}
>>> 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'}
作用:它可以扩展函数的功能。比如,在person函数里,我们保证能接收到name和age这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。试想你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数就能满足注册的需求。
和可变参数类似,也可以先组装出一个dict,然后,把该dict转换为关键字参数传进去:
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
**extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra。
- 命名关键字参数
和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。
def person(name, age, *, city, job):
print(name, age, city, job)
\#调用方式如下:
>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer
命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错:
>>> person('Jack', 24, 'Beijing', 'Engineer')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: person() takes 2 positional arguments but 4 were given
高阶函数
- 定义:一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数。编写高阶函数,就是让函数的参数能够接收别的函数。如
def add(x, y, f):
return f(x) + f(y)
map()
- 定义:map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。
def f(x):
return x*x
r = map(f,list(range(0,10)))
print(list(r))
- map()传入的第一个参数是f,即函数对象本身。由于结果r是一个Iterator,Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list。
reduce()
- 定义:reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
>>> from functools import reduce
>>> def fn(x, y):... return x * 10 + y
...
>>> def char2num(s):... return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
...
>>> reduce(fn, map(char2num, '13579'))
13579
- 利用map和reduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456:
- from functools import reduce
def str2float(s):
def char2num(c):
return {'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}[c]
idx = s.index('.')
s = s.replace('.','')
return reduce(lambda x,y:x*10+y,map(char2num,s))/10**(len(s)-idx)
print('str2float(\'123.456\') =',str2float('123.456'))
或者
from functools import reduce
def char2num(c):
return {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}[c]
def mysum(x,y):
return x * 10 + y
def func(s):
idx = s.index('.')
s = s.replace('.','')
result = reduce(mysum,map(char2num,s))/10**(len(s)-idx)
return result
print(func('123.456'))
filter
- 定义:filter()函数用于过滤序列。filter()也接收一个函数和一个序列。filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
- 示例:
在一个list中,删掉偶数,只保留奇数,可以这么写:
def is_odd(n):
return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]
把一个序列中的空字符串删掉,可以这么写:
def not_empty(s):
return s and s.strip()
list(filter(not_empty, ['A', '', 'B', None, 'C', ' ']))
# 结果: ['A', 'B', 'C']
sorted
- sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:
sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
请用sorted()对上述列表分别按名字排序:L2 = sorted(L,key = lambda t:t[0])
再按成绩从高到低排序:
L3 = sorted(L,key = lambda t:t[1],reverse = True)
返回函数
- 定义:把函数作为结果值返回。
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>
调用函数f时,才真正计算求和的结果:
>>> f()
25
- 在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
匿名函数
- 定义:不需要显式地定义函数。关键字lambda表示匿名函数,冒号前面的x表示函数参数。
- 匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
通过对比可以看出,匿名函数lambda x: x * x实际上就是:
def f(x):
return x * x
装饰器
偏函数
- 偏函数,把函数部分的参数固定下来,相当于为部分的参数添加了一个固定的默认值,形成一个新的函数并返回
- 从partial生成的新函数,是对原函数的封装
- https://blog.csdn.net/appleyk/article/details/77609114
模块和包
- 一个.py文件就称之为一个模块(Module)。
按目录来组织模块的方法,称为包(Package)。
定义一个模块
'a test module'
__author__ = '...'
作用域
- 正常的函数和变量名是公开的(public),可以被直接引用,比如:abc,x123,PI等;
- 类似xxx这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的author,name就是特殊变量,hello模块定义的文档注释也可以用特殊变量doc访问,我们自己的变量一般不要用这种变量名;
- 类似_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc,__abc等;
类和实例
- 定义:
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))
·
class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。
·
注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
·
有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:
实例属性和类属性
python是动态语言,根据类创建的实例可以任意绑定属性。- 实例绑定属性:s.score = 90
类绑定属性:直接在class中定义属性。这种属性时类属性,归类所有.这个属性虽然归类所有,但类的所有实例都可以访问到。
class Student(object): name = 'Student'
千万不要把实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性
限制添加的属性:
定义一个特殊的slots变量,来限制该class实例能添加的属性:
使用slots要注意,slots定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:
除非在子类中也定义slots,这样,子类实例允许定义的属性就是自身的slots加上父类的slots.
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
- 区别静态语言:
和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同:
>>> bart = Student('Bart Simpson', 59)
>>> lisa = Student('Lisa Simpson', 87)
>>> bart.age = 8
>>> bart.age
8
>>> lisa.age
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'age'
- 使用@property
Python内置的@property装饰器就是负责把一个方法变成属性调用的
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
`
@property的实现比较复杂,我们先考察如何使用。把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作:
>>> s = Student()
>>> s.score = 60 # OK,实际转化为s.set_score(60)>>> s.score # OK,实际转化为s.get_score()60>>> s.score = 9999
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!
绑定方法:
给实例绑定方法:定义的函数一定要有参数self.给一个实例绑定的方法,对另一个实例是不起作用的:
def set_age(self, age): # 定义一个函数作为实例方法 self.age = age from types import MethodType s.set_age = MethodType(set_age, s) # 给实例绑定一个方法 s.set_age(25) # 调用实例方法 s.age # 测试结果25
给class绑定方法,所有实例都可以调用。函数原型同样需要self
def set_score(self, score): self.score = score Student.set_score = set_score
私有变量:
把属性的名称前加上两个下划线__
如果有一个下划线开头的实例变量_name,也视为私有变量打印实例 str()||repr()
定义好str()方法
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name: %s)' % self.name
>>> print(Student('Michael'))
Student object (name: Michael)
str()返回用户看到的字符串,而repr()返回程序开发者看到的字符串,也就是说,repr()是为调试服务的。
再定义一个repr()。但是通常str()和repr()代码都是一样的
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
__repr__ = __str__
- 打印不存在的属性时:getattr
def __getattr__(self, attr):
if attr=='score':
return 99
继承
- 多重继承
class Dog(Mammal, Runnable)
- 多重继承
通过type()动态创建类
>>> def fn(self, name='world'): # 先定义函数【注意添加self参数】
... print('Hello, %s.' % name)
...
>>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
>>> h = Hello()
>>> h.hello()
Hello, world.
>>> print(type(Hello))
<class 'type'>
>>> print(type(h))
<class '__main__.Hello'>
要创建一个class对象,type()函数依次传入3个参数:
1. class的名称;
2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。
通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。
- metaclass
除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。metaclass,直译为元类,简单的解释就是:先定义metaclass,就可以创建类,最后创建实例。所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。
枚举
- 首先,定义枚举要导入enum模块。
- 枚举定义用class关键字,继承Enum类。
用于定义枚举的class和定义类的class是有区别
示例:
from enum import Enum
class Color(Enum):
red = 1
orange = 2
yellow = 3
green = 4
blue = 5
indigo = 6
purple = 7
`
上面的代码,我们定义了颜色的枚举Color.
颜色枚举有7个成员,分别是Color.red、Color.orange、Color.yellow等。
每一个成员都有它们各自名称和值,Color.red成员的名称是:red,值是:1。
每个成员的数据类型就是它所属的枚举。
- 不可重复:
- 成员名称不可重复:red=1,red=2
- 不同成员的值允许相同,相同的话会看成别名:red=1,red_alias=1,在通过值获得枚举成员时,只会获得第一个成员print(Color(1))->Color.red
- 如果要限制定义枚举时,不能定义相同值的成员,可以使用装饰器@unique(写入unique模块)
python
from enum import Enum, unique
@unique
class Color(Enum):
red = 1
red_alias = 1#提示错误
- 枚举取值
- 1.通过成员名称来获取成员:Color[“red”]
- 2.通过成员值来获取成员:Color(2)
- 迭代遍历
python
for color in Color:
print(color)
# 如果枚举有值重复的成员,循环遍历枚举时只获取值重复成员的第一个成员
# 如果想把值重复的成员也遍历出来,要用枚举的一个特殊属性__members__
for color in Color.__members__.items():
print(color)
调试
try:
print('try...')
r = 10 / 0
print('result:', r)
except ZeroDivisionError as e:
print('except:', e)
finally:
print('finally...')
print('END')
当我们认为某些代码可能会出错时,就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块,执行完except后,如果有finally语句块,则执行finally语句块
单元测试
- 定义:单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
getattr为内置方法,当使用点号获取实例属性时,如果属性不存在就自动调用getattr方法
setattr当设置类实例属性时自动调用,如j.name=5 就会调用setattr方法 self.[name]=5
class Dict(dict):
def __init__(self, **kw):
super().__init__(**kw)
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Dict' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
- unittest模块
import unittest
from mydict import Dict
class TestDict(unittest.TestCase):
def test_init(self):
d = Dict(a=1, b='test')
self.assertEqual(d.a, 1)
self.assertEqual(d.b, 'test')
self.assertTrue(isinstance(d, dict))
def test_key(self):
d = Dict()
d['key'] = 'value'
self.assertEqual(d.key, 'value')
def test_attr(self):
d = Dict()
d.key = 'value'
self.assertTrue('key' in d)
self.assertEqual(d['key'], 'value')
def test_keyerror(self):
d = Dict()
with self.assertRaises(KeyError):
value = d['empty']
- 编写单元测试时,我们需要编写一个测试类,从unittest.TestCase继承。
- 以test开头的方法就是测试方法,不以test开头的方法不被认为是测试方法,测试的时候不会被执行。
- 对每一类测试都需要编写一个test_xxx()方法。由于unittest.TestCase提供了很多内置的条件判断。
- 最常用的断言就是assertEqual():
self.assertEqual(abs(-1), 1) # 断言函数返回的结果与1相等
- 另一种重要的断言就是期待抛出指定类型的Error,比如通过d['empty']访问不存在的key时,断言会抛出KeyError:
with self.assertRaises(KeyError):
value = d[‘empty’]
- 而通过d.empty访问不存在的key时,我们期待抛出AttributeError:
with self.assertRaises(AttributeError):
value = d.empty
- 运行单元测试一旦编写好单元测试,我们就可以运行单元测试。最简单的运行方式是在mydict_test.py的最后加上两行代码:
if __name__ == '__main__':
unittest.main()
这样就可以把mydict_test.py当做正常的python脚本运行
with
- (个人认为with也是调试测试的一部分吧)
- with 语句:
基本思想是with所求值的对象必须有一个__enter__()方法,一个__exit__()方法。紧跟with后面的语句被求值后,返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()方法。 - 例子:
class Sample:
def __enter__(self):
print "In __enter__()"
return "Foo"
def __exit__(self, type, value, trace):
print "In __exit__()"
def get_sample():
return Sample()
with get_sample() as sample:
print "sample:", sample
·
输出:
In__enter__()
sample:Foo
In__exit__()
分析:
2.1.__enter__()方法被执行
2.__enter__()方法返回的值 - 这个例子中是"Foo",赋值给变量'sample'
3.执行代码块,打印变量"sample"的值为 "Foo"
- __exit__()方法被调用 with真正强大之处是它可以处理异常。
class Sample:
def __enter__(self):
return self
def __exit__(self, type, value, trace):
print "type:", type
print "value:", value
print "trace:", trace
def do_something(self):
bar = 1/0
return bar + 10
with Sample() as sample:
sample.do_something()
`
bash-3.2$ ./with_example02.py
type: <type 'exceptions.ZeroDivisionError'>
value: integer division or modulo by zero
trace: <traceback object at 0x1004a8128>
Traceback (most recent call last):
File "./with_example02.py", line 19, in <module>
sample.do_something()
File "./with_example02.py", line 15, in do_something
bar = 1/0
ZeroDivisionError: integer division or modulo by zero
实际上,在with后面的代码块抛出任何异常时,exit()方法被执行。正如例子所示,异常抛出时,与之关联的type,value和stack trace传给exit()方法,因此抛出的ZeroDivisionError异常被打印出来了。开发库时,清理资源,关闭文件等等操作,都可以放在exit方法当中。
文件操作/IO
读文件
打开:
- f = open(‘/Users/michael/test.txt’,’r’)标示符’r’表示读,这样,我们就成功地打开了一个文件。如果文件不存在,open()函数就会抛出一个IOError的错误,并且给出错误码和详细的信息告诉你文件不存在
- 使用with打开:with open(‘/path/to/file’, ‘r’) as f:
要读取二进制文件,比如图片、视频等等,用’rb’模式打开文件即可:f = open(‘/Users/michael/test.jpg’, ‘rb’)
读取:
- Python把内容读到内存,用一个str对象表示。
关闭:
- 最后一步是调用close()方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的:f.close()
字符编码:
- 要读取非UTF-8编码的文本文件,需要给open()函数传入encoding参数,例如,读取GBK编码的文件:
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk')
f.read()
'测试'
- 遇到有些编码不规范的文件,你可能会遇到UnicodeDecodeError,因为在文本文件中可能夹杂了一些非法编码的字符。遇到这种情况,open()函数还接收一个errors参数,表示如果遇到编码错误后如何处理。最简单的方式是直接忽略:
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')
写文件
打开:
- 写文件和读文件是一样的,唯一区别是调用open()函数时,传入标识符’w’或者’wb’表示写文本文件或写二进制文件:
f = open('/Users/michael/test.txt', 'w')
f.write('Hello, world!')
f.close()
- 你可以反复调用write()来写入文件,但是务必要调用f.close()来关闭文件。当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用close()方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()的后果是数据可能只写了一部分到磁盘,剩下的丢失了。所以,还是用with语句来得保险
写入:要写入特定编码的文本文件,请给open()函数传入encoding参数,将字符串自动转换成指定编码:with open(r’C:\Users\56989\Desktop\text.txt’,’w’,encoding=”gbk”) as f:
StringIO
写入StringIO
- StringIO顾名思义就是在内存中读写str。要把str写入StringIO,我们需要先创建一个StringIO,然后,像文件一样写入即可:
>>> from io import StringIO
>>> f = StringIO()
>>> f.write('hello')
5>>> f.write(' ')
1>>> f.write('world!')
6>>> print(f.getvalue())
hello world!
读出StringIOn
- getvalue()方法用于获得写入后的str。
>>> from io import StringIO
>>> f = StringIO('Hello!\nHi!\nGoodbye!')
>>> while True:
... s = f.readline()
... if s == '':
... break... print(s.strip())
...
Hello!
Hi!
Goodbye!
同理二进制的BytesIO
序列化
- 定义:把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等
- 序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。
- 反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。
写入: - pickle.dumps()方法把任意对象序列化成一个bytes,然后,就可以把这个bytes写入文件。
import pickle
d = dict(name = 'Bob',age = 20, score = 88)
with open('dump.txt','wb') as f:
pickle.dump(d,f)
- pickle.loads()方法反序列化出对象
>>> f = open('dump.txt', 'rb')
>>> d = pickle.load(f)
>>> f.close()
>>> d
{'age': 20, 'score': 88, 'name': 'Bob'}
JSON
- 定义:一种序列化标准格式,比XML更快,而且可以直接在web页面中读取。
- 将python对象变成一个JSON:
>>> import json
>>> d = dict(name='Bob', age=20, score=88)
>>> json.dumps(d)
'{"age": 20, "score": 88, "name": "Bob"}'
dumps()方法返回一个str,内容就是标准的JSON。
- 将JSON反序列化为python对象:
>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> json.loads(json_str)
{'age': 20, 'score': 88, 'name': 'Bob'}
- 将对象转变成JSON格式:
def student2dict(std):
return {
'name': std.name,
'age': std.age,
'score': std.score
}
`
print(json.dumps(s, default=student2dict))
{"age": 20, "name": "Bob", "score": 88}
`
不过,下次如果遇到一个Teacher类的实例,照样无法序列化为JSON。我们可以偷个懒,把任意class的实例变为dict:
print(json.dumps(s, default=lambda obj: obj.__dict__))
`
因为通常class的实例都有一个__dict__属性,它就是一个dict,用来存储实例变量。也有少数例外,比如定义了__slots__的class。
- 将JSON反序列化为一个对象:
def dict2student(d):
return Student(d['name'], d['age'], d['score'])
结果如下:
>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> print(json.loads(json_str, object_hook=dict2student))
<__main__.Student object at 0x10cd3c190>
进程
- os.getpid()获取当前进程id os.getppid()获取父进程id
- 启动子进程:
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('test'))
print('Child process will start.')
p.start()
p.join()
print('Child process end.')
- 看一下Process类的构造方法:
init(self, group=None, target=None, name=None, args=(), kwargs={})
参数说明:
group:进程所属组。基本不用
target:表示调用对象。
args:表示调用对象的位置参数元组。
name:别名
kwargs:表示调用对象的字典。
#coding=utf-8
import multiprocessing
def do(n) :
#获取当前线程的名字
name = multiprocessing.current_process().name
print name,'starting'
print "worker ", n
return
if __name__ == '__main__' :
numList = []
for i in xrange(5) :
p = multiprocessing.Process(target=do, args=(i,))
numList.append(p)
p.start()
p.join()
print "Process end."
结果:
Process-1 starting
worker 0
Process end.
Process-2 starting
worker 1
Process end.
Process-3 starting
worker 2
Process end.
Process-4 starting
worker 3
Process end.
Process-5 starting
worker 4
Process end.
Pool类
定义:Pool类可以提供指定数量的进程供用户调用,当有新的请求提交到Pool中时,如果池还没有满,就会创建一个新的进程来执行请求。如果池满,请求就会告知先等待,直到池中有进程结束,才会创建新的进程来执行这些请求。
常见函数:
- apply_async(func[, args=()[, kwds={}[, callback=None]]]):非阻塞且支持结果返回进行回调。
- map(func, iterable[, chunksize=None]):Pool类中的map方法,与内置的map函数用法行为基本一致,它会使进程阻塞直到返回结果。 注意,虽然第二个参数是一个迭代器,但在实际使用中,必须在整个队列都就绪后,程序才会运行子进程。
- close():关闭进程池(pool),使其不再接受新的任务。
- terminate():结束工作进程,不在处理未处理的任务。
- join():主进程阻塞等待子进程的退出,join方法必须在close或terminate之后使用。
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
from multiprocessing import Process
import multiprocessing
import time
from multiprocessing import Pool
def run(x):
time.sleep(1)
return x*x
if __name__ == "__main__":
alist = list(range(1,10))
s0 = time.time()
r = map(run,alist)
print(list(r)) #注意惰性性质
e0 = time.time()
print("时间:%d" % int(e0 - s0))
pool = Pool(5)
s1 = time.time()
pool.map(run,alist)
pool.close()
pool.join()
e1 = time.time()
print("时间:%d" %int(e1-s1))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
时间:9
时间:7
# -*- coding:utf-8 -*-
import multiprocessing
import time
def func(msg):
print "*msg: ", msg
time.sleep(3)
print "*end"
if __name__ == "__main__":
# 维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
pool = multiprocessing.Pool(processes=3)
for i in range(10):
msg = "hello [{}]".format(i)
# pool.apply(func, (msg,))
pool.apply_async(func, (msg,)) # 异步开启进程, 非阻塞型, 能够向池中添加进程而不等待其执行完毕就能再次执行循环
print "--" * 10
pool.close() # 关闭pool, 则不会有新的进程添加进去
pool.join() # 必须在join之前close, 然后join等待pool中所有的线程执行完毕
print "All process done."
结果:
*msg: hello [0]
*msg: hello [1]
*msg: hello [2]
*end
*msg: hello [3]
*end
*end
*msg: hello [4]
*msg: hello [5]
*end
*msg: hello [6]
*end
*end
*msg: hello [7]
*msg: hello [8]
*end
*msg: hello [9]
*end*end
*end
All process done.
Process finished with exit code 0
Queue模块
在python中,多个线程之间的数据是共享的,多个线程进行数据交换的时候,不能够保证数据的安全性和一致性,所以当多个线程需要进行数据交换的时候,队列就出现了,队列可以完美解决线程间的数据交换,保证线程间数据的安全性和一致性
queue模块的三种队列及构造函数:
- Python queue模块的FIFO队列先进先出。 class queue.Queue(maxsize)
- LIFO类似于堆,即先进后出。 class queue.LifoQueue(maxsize)
- 优先级队列级别越低越先出来。 class queue.PriorityQueue(maxsize)
常用方法:
- queue.qsize() 返回队列的大小
- queue.empty() 如果队列为空,返回True,反之False
- queue.full() 如果队列满了,返回True,反之False queue.full 与 maxsize 大小对应
- queue.get([block[, timeout]])获取队列,timeout等待时间
- queue.get_nowait() 相当queue.get(False)
- queue.put(item) 写入队列,timeout等待时间
- queue.put_nowait(item) 相当queue.put(item, False)
- queue.task_done() 在完成一项工作之后,queue.task_done()函数向任务已经完成的队列发送一个信号
- queue.join() 实际上意味着等到队列为空,再执行别的操作
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
from multiprocessing import Process,Queue
import multiprocessing
import time
from multiprocessing import Pool
import os
def write(q):
print("Process to write: %s" %os.getpid())
for i in list(range(26)):
c = chr(i+ord('A'))
print('put '+c)
q.put(c)
time.sleep(0.5)
def read(q):
print("Process to read: %s" %os.getpid())
while True:
value = q.get(True)
print('get '+value)
if __name__ == '__main__':
q = Queue()
pw = Process(target=write,args=(q,))
pr = Process(target=read,args=(q,))
pw.start()
pr.start()
pw.join()
pr.terminate()
结果:
Process to write: 10304
put A
Process to read: 11368
get A
put B
get B
put C
get C
put D
get D
put E
get E
put F
get F
put G
get G
put H
get H
put I
get I
put J
get J
put K
get K
put L
get L
put M
get M
put N
get N
put O
get O
put P
get P
put Q
get Q
put R
get R
put S
get S
put T
get T
put U
get U
put V
get V
put W
get W
put X
get X
put Y
get Y
put Z
get Z
线程
- Python的标准库提供了两个模块:_thread和threading,_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。
- 启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行:
- 由于任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程又可以启动新的线程,Python的threading模块有个current_thread()函数,它永远返回当前线程的实例。主线程实例的名字叫MainThread,子线程的名字在创建时指定,我们用LoopThread命名子线程。名字仅仅在打印时用来显示,完全没有其他意义,如果不起名字Python就自动给线程命名为Thread-1,Thread-2……
import time, threading
线程执行的代码:
def loop():
print('thread %s is running...' % threading.current_thread().name)
n = 0
while n < 5:
n = n + 1
print('thread %s >>> %s' % (threading.current_thread().name, n))
time.sleep(1)
print('thread %s ended.' % threading.current_thread().name)
print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.' % threading.current_thread().name)
Lock
- 多线程和多进程的区别:多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。
balance = 0
lock = threading.Lock()
def run_thread(n):
for i in range(100000):
# 先要获取锁:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了一定要释放锁:
lock.release()
- 当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。
- 获得锁的线程用完后一定要释放锁,否则那些苦苦等待锁的线程将永远等待下去,成为死线程。所以我们用try…finally来确保锁一定会被释放
- 锁的好处就是确保了某段关键代码只能由一个线程从头到尾完整地执行,坏处当然也很多,首先是阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了。其次,由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁,导致多个线程全部挂起,既不能执行,也无法结束,只能靠操作系统强制终止。
多核CPU
- python并没有办法把多核全部占满,即使是死循环,也只能占满一个核。这是因为Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。
- 所以,在Python中,可以使用多线程,但不要指望能有效利用多核。如果一定要通过多线程利用多核,那只能通过C扩展来实现,不过这样就失去了Python简单易用的特点。不过,也不用过于担心,Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。
ThreadLocal()
一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。ThreadLocal解决了参数在一个线程中各个函数之间互相传递的问题。
ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。
全局变量local_school就是一个ThreadLocal对象,每个Thread对它都可以读写student属性,但互不影响。你可以把local_school看成全局变量,但每个属性如local_school.student都是线程的局部变量,可以任意读写而互不干扰,也不用管理锁的问题,ThreadLocal内部会处理。
- 可以理解为全局变量local_school是一个dict,不但可以用local_school.student,还可以绑定其他变量,如local_school.teacher等等。
import threading
# 创建全局ThreadLocal对象:
local_school = threading.local()
def process_student():
# 获取当前线程关联的student:
std = local_school.student
print('Hello, %s (in %s)' % (std, threading.current_thread().name))
def process_thread(name):
# 绑定ThreadLocal的student:
local_school.student = name
process_student()
t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
结果:
Hello, Alice (in Thread-A)
Hello, Bob (in Thread-B)
正则表达式
部分规则:
- 单个匹配:
- \d可以匹配一个数字
- \w可以匹配一个字母或数字
- .可以匹配任意字符
- \s可以匹配一个空格(也包括Tab等空白符)
- 变长匹配:
- 用*表示任意个字符(包括0个)
- 用+表示至少一个字符
- 用?表示0个或1个字符
- 用{n}表示n个字符
- 用{n,m}表示n-m个字符:
- 精确匹配:
- [0-9a-zA-Z_]可以匹配一个数字、字母或者下划线
- A|B可以匹配A或B
- ^表示行的开头,^\d表示必须以数字开头。
-
表示行的结束,\d
表
示
行
的
结
束
,
\d
表示必须以数字结束。
re模块
match()
- match()方法判断是否匹配,如果匹配成功,返回一个Match对象,否则返回None。常见的判断方法就是:
test = '用户输入的字符串'if re.match(r'正则表达式', test):
print('ok')
else:
print('failed')
分组:
- 用()表示的就是要提取的分组(Group)
- ^(\d{3})-(\d{3,8})$分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码:
- 实例
>>> m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
>>> m
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
>>> m.group(0)
'010-12345'
>>> m.group(1)
'010'
>>> m.group(2)
'12345'
- 强烈建议使用Python的r前缀,就不用考虑转义的问题了:
>>> import re
>>> re.match(r'^\d{3}\-\d{3,8}$', '010-12345')
<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
>>> re.match(r'^\d{3}\-\d{3,8}$', '010 12345')
>>>
切分字符串split
- 调用方式:re.split(r’\s+’,abc’)或者’abc’.split(‘分割符’)
- 默认会分割空格
- 实例:
1.无论多少个空格都可以正常分割。加入,试试:
>>> re.split(r'[\s\,]+', 'a,b, c d')
['a', 'b', 'c', 'd']
2. 再加入;试试:
>>> re.split(r'[\s\,\;]+', 'a,b;; c d')
['a', 'b', 'c', 'd']
编译
当我们在Python中使用正则表达式时,re模块内部会干两件事情:
1. 编译正则表达式,如果正则表达式的字符串本身不合法,会报错;
2. 用编译后的正则表达式去匹配字符串。
如果一个正则表达式要重复使用几千次,出于效率的考虑,我们可以预编译该正则表达式,接下来重复使用时就不需要编译这个步骤了,直接匹配:
>>> import re
# 编译:
>>> re_telephone = re.compile(r'^(\d{3})-(\d{3,8})$')
# 使用:
>>> re_telephone.match('010-12345').groups()
('010', '12345')
>>> re_telephone.match('010-8086').groups()
('010', '8086')
collections模块
- 定义:Python内的一个集合模块
namedtuple:
- namedtuple(‘名称’, [属性list]):
- namedtuple是一个函数,它用来创建一个自定义的tuple对象,并且规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素。
- 这样一来,我们用namedtuple可以很方便地定义一种数据类型,它具备tuple的不变性。
- 是tuple的一种子类。
>>> from collections import namedtuple >>> Point = namedtuple('Point', ['x', 'y']) >>> p = Point(1, 2) >>> p.x 1 >>> p.y 2 类似的,如果要用坐标和半径表示一个圆,也可以用namedtuple定义: # namedtuple('名称', [属性list]): Circle = namedtuple('Circle', ['x', 'y', 'r'])
deque:
- deque是为了高效实现插入和删除操作的双向列表,适合用于队列和栈。
- 使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低。
>>> from collections import deque >>> q = deque(['a', 'b', 'c']) >>> q.append('x') >>> q.appendleft('y') >>> q deque(['y', 'a', 'b', 'c', 'x'])
defaultdict:
使用dict时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict.
>>> from collections import defaultdict >>> dd = defaultdict(lambda: 'N/A') >>> dd['key1'] = 'abc' >>> dd['key1'] # key1存在'abc' dd['key2'] # key2不存在,返回默认值 'N/A'
注意默认值是调用函数返回的,而函数在创建defaultdict对象时传入。除了在Key不存在时返回默认值,defaultdict的其他行为跟dict是完全一样的。
OrderedDict:
- 使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序.如果要保持Key的顺序,可以用OrderedDict
- 注意,OrderedDict的Key会按照插入的顺序排列,不是Key本身排序.
>>> from collections import OrderedDict >>> d = dict([('a', 1), ('b', 2), ('c', 3)]) >>> d # dict的Key是无序的 {'a': 1, 'c': 3, 'b': 2} >>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)]) >>> od # OrderedDict的Key是有序的 OrderedDict([('a', 1), ('b', 2), ('c', 3)])
Counter:
- Counter是一个简单的计数器,例如,统计字符出现的个数.
- Counter实际上也是dict的一个子类
>>> from collections import Counter >>> c = Counter() >>> for ch in 'programming': c[ch] = c[ch] + 1 >>> c Counter({'g': 2, 'm': 2, 'r': 2, 'a': 1, 'i': 1, 'o': 1, 'n': 1, 'p': 1})
摘要算法
- 它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
```python
import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 in python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
```
- 如果数据量很大,可以分块多次调用update(),最后计算的结果是一样的
```python
import hashlib
md5 = hashlib.md5()
md5.update('how to use md5 in '.encode('utf-8'))
md5.update('python hashlib?'.encode('utf-8'))
print(md5.hexdigest())
```
网络编程
相关概念:
- 互联网协议包含了上百种协议标准,但是最重要的两个协议是TCP和IP协议,所以,大家把互联网的协议简称TCP/IP协议。
- TCP协议则是建立在IP协议之上的。TCP协议负责在两台计算机之间建立可靠连接,保证数据包按顺序到达。TCP协议会通过握手建立连接,然后,对每个IP包编号,确保对方按顺序收到,如果包丢掉了,就自动重发。
- IP地址实际上是一个32位整数(称为IPv4),以字符串表示的IP地址如192.168.0.1实际上是把32位整数按8位分组后的数字表示,目的是便于阅读。
- IPv6地址实际上是一个128位整数,它是目前使用的IPv4的升级版,以字符串表示类似于2001:0db8:85a3:0042:1000:8a2e:0370:7334。
- 一个IP包除了包含要传输的数据外,还包含源IP地址和目标IP地址,源端口和目标端口。
- 端口有什么作用?在两台计算机通信时,只发IP地址是不够的,因为同一台计算机上跑着多个网络程序。一个IP包来了之后,到底是交给浏览器还是QQ,就需要端口号来区分。每个网络程序都向操作系统申请唯一的端口号,这样,两个进程在两台计算机之间建立网络连接就需要各自的IP地址和各自的端口号。
Socket
- 定义:Socket是网络编程的一个抽象概念。通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可。
电子邮件
访问数据库
Web开发
异步IO
他们没有告诉你的
input()返回的是str,如果需要整数,需要int(input())
且运算是 and 同理 or
dir(math)这句可查看所有函数名列表
help(math)查看具体定义及函数原型
输出数据类型:type()
写在开头
#!/usr/bin/python
# -*- coding: UTF-8 -*-
#!/usr/bin/env python与#!/usr/bin/python的区别
脚本语言的第一行,目的就是指出,你想要你的这个文件中的代码用什么可执行程序去运行它.
#!/usr/bin/Python是告诉操作系统执行这个脚本的时候,调用/usr/bin下的python解释器;
#!/usr/bin/env python这种用法是为了防止操作系统用户没有将python装在默认的/usr/bin路径里。当系统看到这一行的时候,首先会到env设置里查找python的安装路径,再调用对应路径下的解释器程序完成操作。
#!/usr/bin/python相当于写死了python路径;
#!/usr/bin/env python会去环境设置寻找python目录,推荐这种写法
- str(num):将浮点数,整数转换成字符串
float(str),int(str):将字符串转换成数字
关于if __name__ \=\= ‘__main__’
if __name__ \=\= ‘__main__’的意思是:当.py文件被直接运行时,if __name__ \=\= ‘__main__’之下的代码块将被运行;当.py文件以模块形式被导入时,if __name__ \=\= ‘__main__’之下的代码块不被运行。
python xxx.py,直接运行xxx.py文件
python -m xxx.py,把xxx.py当做模块运行- 上升到文件于包的关系也是同理,名为__main__.py的会先执行,__main__.py文件是一个包或者目录的入口程序。不管是用python package还是用python -m package运行,__main__.py文件总是被执行。