高阶函数:将函数作为参数
sortted()它还可以接收一个key函数来实现自定义的排序,reversec参数可反转排序
如sorted([4,3,8,1,7,3],key=abs,reverse=true)
返回函数:将函数最为返回值
闭包,相关参数和变量都保存在函数中,返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
解析名称时首先检查局部作用域,然后由内而外一层层检查外部嵌套函数定义的作用域,如找不到搜索全局命令空间和内置命名空间。
global可改变变量作用域,全局变量
nonlocal在函数范围内可用,用在闭包中。
python2 中,解决方法可以是是把修改值放到列表或字典中,python3 中,可以使用nonlocal 声明完成修改
匿名函数:不需要显示定义的函数。
lambda表示匿名函数,lambda x:x*x 其中冒号前x为参数,只能有一个表达式,返回值就是表达式的结果。没有名字不需担心名字冲突。匿名函数也可做为函数返回值。
f=lambda x:x+1 f(1)
g=lambda x,y:x+y g(1,2)
函数也是一个对象,函数对象可以被复制给变量
装饰器:在代码运行期间动态加功能的方式,称之为装饰器
其实这一节的内容理解了以后很简单。
其实就是一种语法的理解与使用:
@xxx #装饰器语法
def func(): #函数
pass
本质就是廖老师讲到的一句话:
func=xxx(func)
本质:把我们的函数作为参数传到装饰器函数后,装饰器函数对它进行修改,然后把名字什么的全部修改到和原来一样,这样就完成了不露痕迹的“函数改造”过程。使用:使用的时候就不用总是纠结本质啦,本质只需要理解一次就够了。我们只需要知道,使用了装饰器,它往我们的函数加了一些代码,把它改造了一下,这就够了。(详细的回去好好看廖老师教程)举个栗子:比如说老师所用到的这个函数:@functools.wraps(func)它做了什么呢?我们回到完整代码看一下:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw): #看这里!
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
那么它做了什么呢?它就是把wrapper的__name__给改了,其他的wrapper的代码原封不动。总结:装饰器就是一种可以修改我们函数功能的语法糖(像糖果一样可口对吧),我们使用时不需要纠结本质,自己编写装饰器时才需要考虑本质,不过也不难对吧。
偏函数:当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原来的部分参数,从而在调用时更简单。
int2=functools.partial(int,base=2)
创建偏函数实际上可以接收函数对象、*args、**kw 这3个参数
int2(‘1001’)等价于
kw={‘base’:2}
int(‘1001’,**kw)
max2=functools.partial(max,10)等价于
args=(10,3,5,6)
max(*args)
模块:一个py文件就是一个模块
模块的好处是大大提高了代码的可维护性,可复用性,有自己的命名空间避免函数名和变量名的冲突。
为避免模块名冲突,python引入了按目录来组织模块的方法,称为包package。假设我的abc和xyz两个模块与其他模块冲突了,于是可通过包来组织模块。方法是选择一个顶层包名,mycompany的组织方式
mycompany
├─ __init__.py
├─ abc.py
└─ xyz.py
注意:每一个包目录下面都会有一个__init__.py的文件,否则python把这个目录当成普通目录,而不是一个包,__init__.py可以空,也可以有代码,因为__init__.py就是一个模块,它的模块名就是mycompany。
使用模块:文件模版
#!/usr/bin/env python3 #使用python3解释器
# -*- coding: utf-8 -*- #使用UTF-8编码
‘a test module’
__author__="royal"
import sys
def test():
pass
if__name__=='__main__': #在单独运行时进行测试的代码
test()
作用域:
在python中使用_来表示私有变量。类似_xxx和__xxx这样的函数或变量就是非公开的(private),不应该被直接引用
模块安装:pip install pillow
推荐使用:Anaconda,已集成好常用的第三方工具
模块搜索路径:默认情况下。python解释器会搜索当前目录,所有已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中:
添加自己的搜索目录,一、修改sys.path目录,运行结束后实效;二、设置环境变量PYTHONPATH。
面向对象编程:OOP
面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的。Class是一种抽象概念,比如我们定义的Class——Student,是指学生这个概念,而实例(Instance)则是一个个具体的Student,比如,Bart Simpson和Lisa Simpson是两个具体的Student。
所以,面向对象的设计思想是抽象出Class,根据Class创建Instance。
面向对象的抽象程度又比函数要高,因为一个Class既包含数据,又包含操作数据的方法。
数据封装、继承和多态是面向对象的三大特点。
类和实例:class & instance
class Student(object): #类名以大写字母开头,括号里的object是所继承的类
def __init__(self,name,score): #构造方法,第一个参数一定是self,表示创建实例本身
self.name=name
self.score=score
和普通函数相比,在类中定义的函数第一个参数一定是self,在调用时,不用传递该参数。
数据封装:
类是创建实例的模版,而实例则是一个一个具体的对象,各个实例拥有数据相互独立,互补影响。
方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例数据。
访问限制:如果让内部属性不被外部访问,可以把属性设置为私有变量,以__开头(双下滑线)。通过get和set方法可设置私有变量的值。
变量名类似__xxx__,是特殊变量,可以直接访问吗,不是private变量。
以一个下划线开头的变量名,外部可以访问,但安装约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
双下划线开头的变量,不能直接访问,但是可用_Stuednt__name来访问。如通过实例royal.__name=‘royal’,和Student内部的__nmae不是一个变量,而是给royal在新增了一个变量
继承和多态:定义一个类时可以从现有的类继承,新的类称为子类,被继承的类称为父类或基类、超类。
子类拥有父类的全部内容,切子类可定义新的方法。子类可覆盖父类的方法,运行时会调用子类的方法。
同一个方法,方法内容不变,根据传入的参数不同,表现出不同的结果。
静态语言vs动态语言
对于静态语言,如需传入Animal类型,则传入的对象必需是Animal类型或它的子类,而动态语言,则不一定要传入Animal类型,只要保证传入的对象有一个run()方法。
动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起来像鸭子”,那它就可以被看作鸭子
获取对象信息
使用type()方法,判断对象类型,type()返回对应class类型
type(‘123’)==str
import types#模块中定义了常量
types.FunctionType\\types.BulitinFunctionType\\types.LambdaType\\types.GeneratorType
使用isinstance()判断是否是实例,检查继承关系。
isinstance([1,2,3],(list,tuple)
总是优先使用isinstance()判断类型,可以将指定类型及其子类以往打尽
dir()可获得一个对象的所有属性和方法,返回一个包含字符串的list
list中类似__xx__的属性和方法有特殊用途,如__len__方法返回长度,在调用len()函数时,在内部是自动去调用对象的__len__()方法
如果自定义的类,想使用len(),需自己写一个__len__()方法。
getattr()、setattr()、hasattr(),可以直接操纵对象
实例属性和类属性
给实例绑定属性的方法是通过实例变量或者self变量
class Student():
name=“student” #类属性,所有的实例均可访问到
def __init__(self,name):
self.name=name #实例属性,要优先于类属性。
Student.age=12 #类属性
def get_age(self):
return self.age
Student.get_age=get_age
实例属性属于各个实例;类属性属于类所有,所有实例共享一个属性;不要将实例属性和类属性设为同名。
使用__slots__:
创建类的实例后,可给实例绑定任何属性和方法。
class Student():
pass
Student.name="royal"#添加类属性
def set_name(self,name):
self.name=name
Student.set_name=set_name #添加类方法,任何实例可用
s=Student()
s.age=12 #添加实例属性
from types import MethodTyoe
def set_age(self,age):
self.age=age
s.set_age=MethodType(set_age,s)#添加实例方法,其他实例不可访问
__slots__变量来限制class实例能添加的属性
class Student():
__slots__=('name','age') #用tuple定于允许绑定的属性名称,仅能用于实例限制属性,对继承子类不起作用,除非在子类中也定于__slots__,这样子类实例允许定义的属性是自身的slots加上父类的slots
使用@property:
python内置的@property装饰器就是负责把一个方法变成属性调用,保证对参数进行必要的检测
把一个getter方法变成属性,只需要加上@proper就可以,此时,@property本身有创建了另一个装饰器@Score.setter,负责把一个setter方法变成属性赋值,
class Screen(object):
@property
def width(self):
return self.width
@width.setter
def width(self,width):
self.width=width
#####
s=Screen()
s.width=1024 #s.setter_width(1024)
s.height=768 #s.setter_height(765)
s.width #s.getter_width()
多重继承:
一个子类可以同时获得多个父类的功能
class Dog(Mammal,Runnable): #语法规则:括号中的继承的多个父类,可以多个
pass
MixIn:目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn功能,而不是设计多层次的复杂的继承关系。
拓扑排序:是一个有向无环图DAG(Directed acyclic graph)的所有定点的线性序列。且该序列必需满足以下条件:1、每个定点出现且只出现一次,2、若存在一条顶点A到顶点B的路径,那么在序列中A出现在B的前面。
从DAG途中选择一个没有前驱(入度为0)的顶点并输出;
从图中删除该顶点和所有以它为起点的有向边。遇到有多个入度为0的时候,按最左原则取就行了,
重复1和2直到当前DAG图为空或当前途中不存在无前驱的顶点为止,后一种说明有向图中必然存在环。
python继承顺序遵循C3算法,只要在一个地方找到所需的内容,就不再继续查找
定制类:
__slots__ 限制实例的属性范围
__len__() 调用len()时会调用该方法
__str__(),用户看到的字符串,相当于java中的toStrig方法,直接print()时调用
__repr__(),返回开发者看到的字符串,是为调试服务的,可以将它和__str__()写成一致,即__repr__=__str__
__iter__() 返回一个迭代对象,然后python会不断调用该迭代对象的__next__()方法得到循环的下一个值,遇到StopIteration时退出循环
__getitem__(),像list一样获取元素,如s[1]是调用
__setitem__,设置元素值,在赋值时调用
__delitem__,删除元素,在del 时调用
__getattr__,动态返回一个属性,当实例中有该属性是直接返回,如没有则在__getattr__中查找,有,这不在getattr中查找。
__call__,直接对实例进行调用,s()是调用该方法。可以把对象看成函数,把函数看成对象。
如何判断一个变量是对象还是函数,使用callable(),可以判断一个对象是否可调用。
枚举类型
Enum可以把一组相关常量定义在一个class里,且class不可变,而且成员可以直接比较。
from enum import Enum,unique
@unique
class Weekday(Enum):
Sun=0 #默认value值从1开始,此初被设置为0
Mon=1
Tue=2
#######
day1=Weekday.Mon
Weekday['Tue']
Weekday(3)
使用元类:
type()可以查看一个类型或变量的类型,Student是calss,它的类型是type,而s是一个实例,它的类型就是class Hello
type()函数既可以返回一个对象的类型,又可以创建出新的类型
def fn(self,name='world'):
print('Hello, %s !'%name)
Hello=type('Hello',(object,),dict(hello=fn))#第一各参数类名,第二各继承的类,第三个,绑定方法
通过type()创建的类和class的类是一样的,在解释器遇到class定义时,仅仅扫描一下class定义的语法,然后调用type()创建出class
metaclass 元类:
先定义metaclass,就可以创建类,最后创建实例。可以把类看成是metaclass创建出的实例。
metaclass是类的模版,所以必需从type类型派生。
class ListMetaclass(type):
def __new__(cls, name, bases,attrs):#cls:当前准备创建的类的对象,name:类名,bases:父类;attrs:方法集合
attrs['add']=lambda self,value:self.append(value)
return type.__new__(cls,name,bases,attrs)
class Mylist(list,metaclass=ListMetaclass): #metaclass指定元类,解释器在创建Mylist是调用ListMetaclass.__new__()方法,可以修改累的方法,然后返回修改的定义。
pass
通过metaclass修改类的定义,ORM(对象关系映射)
当用户定义一个class User(Model)时,python解释器首先在当前类User定义中查找metaclass,如果没有找到,就继续在父类Model中查找metaclass,找到了,就使用Model中定义的metaclass的ModelMetaclass来创建User类
metaclass是python中非常具有魔术性的对象,他可以改变类创建时的行为。
错误处理:
try:
code may have error #代码
except Exception as e: #捕获的异常1
do something() #异常时执行
except Exception as r:#捕获的异常2
pass
else:
print(“no error”)
finally:
always exec this party #一定会执行
再有多个exception时,父类的异常会捕获子类的异常,如unicideError是valueError的子类,会被valueError捕获
调用栈:错误没有被捕获就会一直往上抛
出错时,一定要分析错误的调用栈信息,才能定位错误的位置
错误记录:python内置的logging模块可以非常容易的记录错误信息
抛出错误:raise 语句抛出异常
调试:使用断言
使用print(),这样会有大量垃圾代码
使用assert :如果断言失败,会抛出AssertionError异常
assert n!=0: 表达式n!=0为true,否则报错
启动python是可以用 -0参数来关闭assert。
使用:logging不会抛出错误,而且可以输出到文件。他允许指定记录信息的级别,有debug、info、warning、errir几个级别,
import logging
logging.basicConfig(level=logging.INFO)
logging.info("debug")
使用:pdb,让程序单步方式运行。
在运行py文件时, python -m pdb err.py来启动调试器
命令l:查看代码
命令n:单步执行代码
命令p 变量名:查看变量名值
命令q:退出调试
使用pdb.set_trace(),在代码中引入import pdb,在可能出错的地方放置pdb.set_trace()设置断点
程序遇到pdb.set_trace()进入pdb调试环境,命令c:继续运行。
单元测试:TDD 测试驱动开发,测试用例
with语句:有一些任务,可能事先需要设置,事后做清理工作。紧跟with后面的语句被求值后,返回对象的 __enter__() 方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的 __exit__()方法。
with-as表达式极大的简化了每次写finally的工作,功能类似于,try: except : finally:
要使用 with 语句,首先要明白上下文管理器这一概念。有了上下文管理器,with 语句才能工作。
下面是一组与上下文管理器和with 语句有关的概念。
上下文管理协议(Context Management Protocol):包含方法 __enter__() 和 __exit__(),支持该协议的对象要实现这两个方法。
上下文管理器(Context Manager):支持上下文管理协议的对象,这种对象实现了__enter__() 和 __exit__() 方法。上下文管理器定义执行 with 语句时要建立的运行时上下文,负责执行 with 语句块上下文中的进入与退出操作。通常使用 with 语句调用上下文管理器,也可以通过直接调用其方法来使用。
运行时上下文(runtime context):由上下文管理器创建,通过上下文管理器的 __enter__() 和__exit__() 方法实现,__enter__() 方法在语句体执行之前进入运行时上下文,__exit__() 在语句体执行完后从运行时上下文退出。with 语句支持运行时上下文这一概念。
上下文表达式(Context Expression):with 语句中跟在关键字 with 之后的表达式,该表达式要返回一个上下文管理器对象。
语句体(with-body):with 语句包裹起来的代码块,在执行语句体之前会调用上下文管理器的 __enter__() 方法,执行完语句体之后会执行__exit__() 方法。
通过命令行运行:python -m unittest my_test
单元测试中两个特殊的方法setUp()和tearDown(),这两个方法分别在美调用一个测试方法的前后分别被执行。
编写单元测试时,需要编写一个测试类,从unittest.TestCase继承,以test开头的方法就是测试方法,不以test开头的方法不认为是测试方法,测试时不会被执行,对每一各测试类都需要编写test_xxx()方法,由于unittest.TestCase提供了很多内置的条件判断,我们只需要调用这些方法就可以断言输出是否是我们所期望的。最常用的断言就是assertEqual()
文档注释:python内置的“文档测试”模块可以直接提取注释中的代码并执行测试。doctest严格按照python交互式命令行的输入和输出来判断测试结果是否正确。
import doctest #引入doctest模块
doctest.testmod()
IO编程:输入|输出,stream流是一个很重要的概念,inputstream数据流进内存,outputstream数据从内存流到外面去。
同步IO,cpu等待io完成继续执行
异步io,cpu执行其他任务,io完成后通知cpu
文件读写:python内置读写文件的函数,用法和c是兼容的。
在磁盘上读写文件的功能都是有操作系统提供的,现代操作系统不允许普通程序直接操作磁盘,读写文件就是请求操作系统打开一个文件对象(文件描述符),然后通过系统提供的接口从这个文件对象中读取数据或写文件。
try:
f=open('test.txt','r')
print(f.read())
finally:
if f:
f.close()
使用with语句来调用close()
with open(‘test.txt’,'r') as f:
f.read()
每次调用read()会一次性读取文件的全部内容,为安全可反复调用read(size)方法,readline()每次读取一行,readlines()一次读取所有内容并按行返回list。
file_like object类文件对象,即含有read()方法的对象。
二进制文件
读取文本默认是UTF-8编码的文本文件。要读取二进制文件,用‘rb’模式打开
字符编码:要读去非UTF-8的文本文件,各open()函数传入encoding参数。
写文件使用‘w’模式:with open(‘test.text’,'w') as f:
f.write('ssssss')
f.close()
close()必须调用,当写文件是,系统并不立即将数据写入磁盘,而是缓存到内存,调用close时,才保证把数据写入磁盘。
StringIO在内存中读取str,可像文件一样读取
f=StringIO(‘hello,hi googbye’)
f.getvalue()
BytesIO,操作二进制数据
f=BytesIO()
f.write('zh',encode('utf-8'))
seek()用于移动文件读写指针到指定位置
tell()获取当前文件读取指针的位置
操作文件和目录:
python内置os模块,操作文件和目录的一部分放在os模块中,一部分放在os.path模块中,os.path.join()链接目录。os.path.split()函数,可拆分路径,最后一部分总是最后级别的目录或文件名。
os.path.splitext()可以得到文件的扩展名。
序列化:
把变量从内存中变成可存储或传输的过程称为序列化,在python中叫pickling,在其他语言中被称为serialization,marshalling,flattening。python提供了pickle模块实现序列化。
import pickle
d=[1,2,3,4,5]
pickle.dumps(d)#把任意对象序列化为一个bytes,可以把bytes写入文件
f=open('dump.txt','wb')
pickle.dump(d,f)
f.close()
pickle.loads()方法反序列化出对象
可能不同版本的pickle不兼容,因此,只能用pickle保存哪些不重要的数据,不能反序列化也没关系。
JSON:在不同的编程语言间传递对象,就必须把对象序列化为标准格式,如xml,json。python提供了json模块
import json
d=[1,2,3]
json.dumps(d)
json.dump()#写入类文件对象
json.loads()#反序列化json对象
json.load()
dict对象可以直接序列化为JSON的{},
python类序列化为json,类不是一个可序列化的JSON对象。使用json.dumps()方法的可选参数进行序列化。
可为需要序列化的写一个转换函数例如:
def student2dict(std):
return {'name':std.name,'age':std.age}
json.dump(std,default=student2dict)可继进行序列化。
或者写成json.dumps(std,default=lambda ojb:obj.__dict__)
同样反序列化一个对象,需传入object_hook函数负责把dict转为对象实例。
def dict2student(d):
return Studeng(d['name'],d['age'])
json.loads(json_str,object_hook=dict2student)
进程:
线程是最小的执行单位,而进程由至少一个线程组成,如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间。
unix、linux系统提供了一个fork()系统调用,分别在父进程和子进程内返回。
windows系统没有fork调用,在windows版上os.fork()无效。
multiprocessing模块是跨平台的多进程模块。
创建子进程式,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,join()方法可以等待子进程结束后继续运行下去,通常用于进程间的同步。
p=Process(target=run_sub,args=('test',))
pool进程池的方式创建子进程:
Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必需先调用close(),调用close()后就不能继续添加新的process了。pool的默认大小是CPU的核数,如果有8核cpu,须提交至少9各子进程才能看到等待效果。
子进程,subprocess模块可以非常方便的启动一个子进程,然后控制其输入和输出。通过子进程来执行外部指令,并通过imput、output、error管道,获取子进程的执行返回信息。如执行windows的dir命令
subprocess.call(['nslookup','www.python.org'])
subprocess.Popen(['nslookup'],stdin=subprocess.PIPE,stdout=subprocess.PIPE)
进程间通信
mutilprocess模块包装了底层的机制,提供了Queue,pipes等多种方式来交换数据。multiprocess模拟出fork的效果,父进程所有python对象都必须通过pickle序列化再传入到子进程去,所以,如果multiprocess在windows下调用失败,要先考虑是不是pickle失败了。
多线程:
多任务可以由多进程完成,也可以由一个进程内的多线程完成。python提供了两个模块:_thread和threading,_thread是低级模块,threading是高级模块,对——thread进行了封装,使用threading模块就行了。
启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行。任何进程默认会启动一个线程,称为主线程,主线程可以启动新的线程。threading模块的current_thread()函数返回当前线程的实例
threading.Thread(target=loop,name='loopthread')
Lock:多线程和多进程不同,多进程中,同一变量,各自有一份拷贝存在于每各进程总互补影响,而线程中,所有变量都由线程共享,任何变量都可以被任何一个线程修改。
当多个线程同时执行lock.acquire()时,只有一个线程能成功地获得锁,其他线程就继续等待直到获得锁为止。
获得锁的线程用完后一定要释放锁,否则其他线程一直等待,使用try finally来确保锁一定会被释放。锁的好处是保存关键代码只由一个线程执行,坏处:阻止了线程并发,包含锁的代码实际上是单线程模式执行,效率大大降低,如果有多个锁存在,可能造成死锁。
多核cpu:
python解释器执行代码是,有一个GIL(global interpreter Lock),任何python线程执行前,必需先获得GIL锁,每执行100条字节码,解释器就释放GIL锁,让别的线程有机会执行,GIL锁实际上把所有的线程的执行代码都给锁上了,所以,多线程在python中只能交替执行。实际只用到一个核。可通过多进程来实现多核任务,多个python进程有各自独立的GIL锁,互不影响。
ThreadLocal:在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好。
exp=threading.local()
exp是ThreadLocal对象,是一个全局变量,但每个线程只能读写自己线程独立的副本,互不干扰。ThreadLocal解决了参数在一个线程中各个函数之间相互传递的问题。
进程Vs线程
多任务的两种方式:多进程和多线程。如果用多进程实现Master-worker,主进程就是Master其他进程就是worker。如果用多线程实现Master-worker,主线程就是master,其他线程就是worker。
多进程模式最大的有点就是稳定性高,缺点是创建代价大,过多的线程切换消耗资源。计算密集性任务即使用CPU多的任务,使用像C语言效率高。IO密集型任务;使用脚本语言如python开发效率高。
异步IO,单线程的异步编程模型称为协程
分布式进程:
在Thread和process中,应到优选Process,因为Process更稳定,而且Process可以分布到多台机器上,而thread最多只能分布到同一台机器的多个CPU上。
python的multiprocessing模块支持多进程,其中的manager子模块支持分布式进程。一个服务进程作为调度者,将任务分布到其他多个进程中,依靠网络通信。
通过Queue可进行进程的通信,通过managers,模块把Queue通过网络暴露出去,就可以让其他机器的进程访问Queue
master通过manager将task与result暴露到网络上,worker通过manager获取task和result队列。
Queue是用来传递任务和结束结果,每个任务的描述数据量要尽量小。
正则表达式:
正则表达式是一种用来匹配字符串的有力武器,用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串就认为匹配。
1、如果直接给出字符,就是精确匹配。如'\d'匹配一个数字,‘00\d’可匹配‘007’等
2、'.'(点)可匹配任意单个字符,‘py.’ 可匹配‘pyc’、‘py!’
3、匹配变长字符,‘*’表示前面的子表达式任意各字符(包括0个)
4、有+表示前面的子表达式至少一个字符
5、用?表示前面的子表达式0个或1个字符
6、用{n}表示n各字符,用{n,m}表示n-m个字符。
如\d{3}\s+\d{3,8},
\d{3}表三个数字,\s+表至少一个空格,\d{3,8}表3-8各数字
7、可用[ ]表示范围,如[0-9a-zA-Z\_]匹配一个数字、字母或下划线
[0-9A-Z\_]+表示可以匹配至少有一个数字、字母、化下划线组成的字符。
[a-zA-Z\_][0-9A-Z\_]{0,19} 匹配1-19个字符。
8、A|B 匹配A或B。
9、^表示行的开头,^\d表现必须以数字开头
10、$表示行的结束,\d$表示必需已数字结束
11、()标记一个子表达式的开始和结束位置
正则表达是基本形式:^([]{})([]{})$。上面的都是简写
python的re模块
pyhton字符串本身使用\转义。使用python的r前缀,不用考虑转义问题,如s='ABC\\-001'和s=r'ABC\-001'
re.match(r'abc','abc'),如匹配成功,返回一个Match对象,否则返回NONE
用途
1、切分字符串,re.split(r'\s\,\;','a,b;;c d')
2、分组,用()提取的分组
m=re.match('r^(\d{3})-(\d{3,8})$','010-11231')
m.groups() #返回全部分组
m.group(0) #原字符串
m.group(1) #第一各子串
m.group(2) #第二个子串
贪婪匹配:默认+、*是贪婪匹配,即最长匹配,*、+限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个?就可以实现非贪婪或最小匹配
re.match(r'^(\d+?)(0*)$',''102300'')
编译:
python在使用正则表达式时,re模块会
1、编译正则表达式,如果正则表达式的字符串不合法,报错
2、用编译后的正则表达式去匹配字符串
如果一个正则表达要重复使用多次,可已进行预编译正则表达式。
re_telephone=re.complie(r'^(\d{3})-(\d{3,8})$')
re_telephone.match('010-123123')
内建模块:
datetime是python处理日期的标准库
import datetime import datetime
now=datetime.now()#返回当前时间,是datetime类型
获取指定时间
de=datetime(2015,4,15,12,20)
datetime转换timestamp
把1970年1月1日00:00:00称为epoch time,记为0(1970年前的时间timetamp为负数)。时间戳与时区无关。
datetime.now().timestamp()
pythond的timestamp是一个浮点数。
timestamp转换为datetime
#转换为本地时间即与时区相关
datime.fromtimestamp(t)
#转换为格林威治时间
datetime.utcfromtimestamp(t)
str转为datetime strptime(f)需要日期时间格式化的字符串
datetime.strptime('2016-1-1 18:12:12','%Y-%m-%d %H:%M:%S')
datetime转为str strftime(f)需要日期时间的格式化字符串
datetime.now().strftime('%a,%b %d %H:%M')
datetime加减 timedetla模块
now=datetime.now()+timedetla(hours=10)
本地时间转换为UTC时间
datetime有一个时区属性tzinfo,默认是none。
tz_utc_8=timezone(timedetla(hours=8))
datetime.now().replace(tzinfo=tz_utc_8)
时区转换:拿到datetime设置时区
utcnow()拿到当前的UTC时间,在转换为任意时区的时间。
utc_dt=datetime.utctime().replace(tzinfo=timezone.utc)
bj_dt=utc_dt.astimezone(timezone(timedelta(hours=8)))
rb_dt=bj_dt.astimezome(timezone(timedelta(hours=9)))
利用带时区的datime,通过astimezone()放啊分,转换任意时区
不必从UTC+0:00时区转换到其他时区,任何带时区的datetime都可以正确转换如,bj_dt到rb_dt的转换
datime时间需要时区信息才能确定一个特定的时间,否则只能视为本地时间。如果需要存储datetime,最佳方法是将其转换为timestamp在存储,因为timestamp与时区无关。
转换时区的关键在于,拿到一个datetime时,要获知其正确的时区,然后强制设置时区,作为基准时间
collections是python内建的一个集合模块,提供了许多有用的集合类
namedtuple命名元组
from collections import namedtuple
Point=namedtuple(‘Point’,['x','y'])
p=Point(1,2)
p.x p.y
namedtuple是一个函数,它用来创建一个自定义的tuple,并且规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素。
deque是为了高效实现插入和删除操作的双向列表,适合于队列和栈。
q=deque(['a','b','c'])
q.append('x')
q.appendleft('y')
defaultdict,使用dict时,如果可以引用key不存在,就会抛出keyError,如果不希望key不存在时,返回一个默认值,就可以使用defaultdict。参数为一个可调用对象
dd=defaultdict(lambda :'N/A')
OrderedDict,保持key的顺序。key按照插入的顺序排列,不是key本身的排序。
od=OrderedDict([('a',1),('b',2),('c',3)])
Counter一个简单的计数器,统计字符出现的次数
Counter([1,2,3,4,5])
base64是一种用64个字符来表示任意二进制数据的方法。
Base64原理:准备一个包含64个字符的数组,然后对二进制数据进行处理,每3个字节一组,一共3*8=24bit,划为4组,每组正好6个bit,这样得到4个数字作为索引(2的6次方=64),然后查表,获得相应的4个字符,就是编码后的字符串。如果编码的二进制数据不是3的倍数,最后会剩下1个或2个字节,Base64用\x00\字节去末尾补充,再在编码的末尾加上1个或2个=号,表示补了多少个字节,解码时会自动去掉。
标准的Base64编码后可能出现+和/,在URL中不能直接作为参数,所以有url safe的base64编码。
struct,python没有专门处理字节的数据类型。字节数组=二进制str。python提供struct模块来解决bytes和其他二进制数据类型的转换。
import struct
struct.pack('>I',10240099)
pack将任意数据变为二进制,的第一个参数是处理命令,‘>I’的意思是:>表示字节顺序是big-endian,也就是网络序,I表示4字节无符号整数,后面的参数个数要和处理指令一致。
sturct.unpack('>IH',b'\xf0\xf0\xf0\xf0\x80\x80')
unpack将bytes变成相应的数据类型,将bytes依次变为I:4字节无符号整数,H:2字节无符号整数。
hashlib摘要算法:
python算法hashlib提供了常见的摘要算法,如MD5、SHA1等。摘要算法有称为哈希算法、散列算法、他通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常是16进制的字符串表示)
摘要函数是一个单向函数,反推很困难。
import hashlib
md5=hashlib.md5()
sha1=hashlib.sha1()
md5.update('sssssss')
sha1.update('ssss')
md5.hexdigest()
sha1.hexdigest()
摘要算法用于保存用户名口令。
md5生成128位摘要,sha1生成160位摘要。因为任何摘要算法都是把无限多的数据集合映射到一个有限的集合中,会出现不同内容生产相同摘要的问题。
黑客并非暴力破解,通过常用的简单口令如123456先计算出常用口令的MD5值。
加盐:对原始口令加一个复杂字符串来实现
hmac算法:Keyed-hashing for message authentication,一个标准算法,在计算哈希的过程中,把key混入计算的过程中。hmac算法对所有哈希算法都通用,可用hmac代替我们的salt算法。计算结果和摘要算法的结果一样长。
import hmac
h=hmac.new(salt,message,digest='MD5')
h.hexdigest()
itertools模块提供了非常有用的用于操作迭代对象的函数
import itertools
itertools.count()#无限自然数序列
itertools.cycle('ABC')#把传入的序列无限重复
无限序列可以无限迭代下去,通过takewhile()等函数根据条件判断截取出一个有限序列。
itertools.chain('ABC','xyx')
itertools.groupby('aaaabbbbcccaaa')
contextlib:
实现上下文管理器是通过__enter__和__exit__这两个方法来实现。
python标准库contextlib提供了更简单的写法。
@contestmanager这个decorator接受一个generator,用yield语句把with。。。 as var把变量输出出去,然后,with语句就可以正常地工作了
@contextmanager
def create_query(name):
print(‘begin’)
q=Student(name)
yield q
@closing
用closing()把对象变为上下文对象,
from contextlib import closing
with closing(urlopen('http://www.baidu.com')) as page:
for line in page:
print(page)
urllib提供了一系列用于操作URL的功能
get请求
request.urlopen('https://api/douban/v2/book/219650') as f:
date=f.read()
print('status',f.status,f.reason)
for k,v in f.getheaders():
print('%s:%s'%(k,v))
print('Date:',date.decode('utf-8'))
模拟浏览器发送get请求,需使用Request对象,通过往Request对象添加HTTP头,模拟浏览器。
req=request.Request('http://www.douban.com')
req.add_header('User-Agent','Mozilla/6.0(iPhone:CPU iPhone OS 8_0 like Mac OS X)')
with request.urlopen(req) as f:
print('status:',f.status,f.reason)
POST请求,只需把参数data以bytes形式传入。
req=Request('https://passport.weibo.cn')
req.add_header('Origin','https://passport.weibo.cn')
login_data=parse.urlencode([('username','email'),('passwword','tesat')])
with request.urlopen(req,data=login_data)
handler,通过Proxy去访问网站,需要利用ProxyHandler来处理。
proxy_handler=urllib.request.ProxyHandler({'http':'http://www.example.com'})
proxy_auth_handler=urllib.request.ProxyBasicAuthHandler()
proxy_auth_handler.add_password('realm','host','username','password')
opener=urlilb.request.bulid_opener(proxy_handler,proxy_auth_handler)
XML:
DOM VS SAX:操作xml有两种方法,DOM和SAX,DOM会把整个XML读入内存,解析为树,因此占用内存大,解析慢,有点是可以任意遍历树的节点。SAX是流模式,占用内存小,解析快,缺点是需要自己处理事件。正常情况下,优先考虑SAX,因为DOM太占内存。
python使用SAX,需关心的事件是start_element,end_element,char_data,准备好这三个函数,就可以解析xml
classDefaultSaxHandler(object):
def start_element(self, name, attrs):
print('sax:start_element: %s, attrs: %s'% (name, str(attrs)))
def end_element(self, name):
print('sax:end_element: %s'% name)
def char_data(self, text):
print('sax:char_data: %s'% text)
生产XML最有效的方发是拼接字符串,建议使用Json。
HTML Parser
from html.parser import HTMLParser
from html.entities import name2codepoint
class MyHTMLParser(HTMLParser):
def handle_starttag(self,tag,attrs):
print('<%s>'tag)
def handle_endtag(self, tag):
print(''% tag)
def handle_startendtag(self, tag, attrs):
print('<%s/>'% tag)
def handle_data(self, data):
print(data)
def handle_comment(self, data):
print('')
def handle_entityref(self, name):
print('&%s;'% name)
def handle_charref(self, name):
print('&#%s;'% name)
parser=MyHTMLParser()
parser.feed(html_str)
特殊字符有两种,一种是英文表示的 ;,一种是数字表示的Ӓ;
第三方模块
pillow图像操作
from PIL import Image
im=Image.open('img.jpg')#打开图片
im1=Image.new('RGB',(wdith,height),(255,255,255))#新建图片
draw=ImageDraw.Draw(im1)#绘制图片
im1.save('img1.jpg','jped')#保存图片
requests访问网络资源
import requests
r=request.get('https://www.douban.com',params={'q':'pyhton'})
对于带参数的URL,传入一个dict作为params
r.content#返回的内容
request可以直接获取返回的JSON,要发送POST请求,只需用POST(),传入data参数作为POST的请求参数。可直接传入json,可操作cookies
chardet用来探测编码,python提供了unicode表示str和bytes两种,但对于未知编码的bytes需要猜测其编码。
import chardet
chardet.detect("hello")
psutil是一个用于检索信息的跨平台库,运行进程和系统利用率(CPU、内存、磁盘、网络),实现系统监控,可跨平台使用,支持Linux/UNIX/OSX/Windows,适合运维人员使用
import psutil
psutil.cpu_time()
psutil.test()
virtualenv虚拟环境,用来为一个应用创建一套隔离的python运行环境,解决如应用A用django1.7,而B应用使用django1.9的情况。
#图形界面
python支持多种图形界面的第三方库,包括:Tk、wxwidgets、Qt、GTK等,python自带的库支持Tk的Tkinter,无须安装任何包可直接使用。
Tk是一个图形库,支持多个操作系统,使用Tcl语言开发。Tk会调用操作系统提供的本地GUI。
---
from tkinter import *
class Applocation(Frame):
def __init__(self,master=None):
Frame.__init__(self,master)
self.pack()
self.createWidgets()
def createWidgets(self):
self.nameInput=Entry(self)
self.nameInput.pack()
self.alertButton=Button(self,text='hello',command=self.hello)
self.alertButton.pack()
def hello(self):
name=self.nameInput.get() or 'world'
messagebox.showinfo('message','hello,%s'%name)
app=Application(master=None)
app.master.title('hello world')
app.mainloop()
GUI中的每个Button、lable、输入框,都是一个widget,Frame则是可以容纳其他widget的widget,所有的weidget组合起来就是一颗树。
pack()将widget加入到父容器中,并实现布局。python内置的Tkinter可以满足基本的GUI需求,如果非常复杂的GUI程序,建议用操作系统原生支持的语言和库来编写。