python怎么复习_Python复习 基础知识

读文件

要以读文件的模式打开一个文件对象,使用Python内置的open()函数,传入文件名和标示符:

>>> f = open('/Users/michael/test.txt', 'r')

标示符'r'表示读,这样,我们就成功地打开了一个文件。

如果文件不存在,open()函数就会抛出一个IOError的错误,并且给出错误码和详细的信息告诉你文件不存在:

>>> f=open('/Users/michael/notfound.txt', 'r')

Traceback (most recent call last):

File "", line 1, in

IOError: [Errno 2] No such file or directory: '/Users/michael/notfound.txt'

如果文件打开成功,接下来,调用read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示:

>>> f.read()

'Hello, world!'

最后一步是调用close()方法关闭文件。文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且操作系统同一时间能打开的文件数量也是有限的:

>>> f.close()

由于文件读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,我们可以使用try ... finally来实现:

try:

f = open('/path/to/file', 'r')

print f.read()

finally:

if f:

f.close()

但是每次都这么写实在太繁琐,所以,Python引入了with语句来自动帮我们调用close()方法:

with open('/path/to/file', 'r') as f:

print f.read()

这和前面的try ... finally是一样的,但是代码更佳简洁,并且不必调用f.close()方法。

调用read()会一次性读取文件的全部内容,如果文件有10G,内存就爆了,所以,要保险起见,可以反复调用read(size)方法,每次最多读取size个字节的内容。另外,调用readline()可以每次读取一行内容,调用readlines()一次读取所有内容并按行返回list。因此,要根据需要决定怎么调用。

如果文件很小,read()一次性读取最方便;如果不能确定文件大小,反复调用read(size)比较保险;如果是配置文件,调用readlines()最方便:

for line in f.readlines():

print(line.strip()) # 把末尾的'\n'删掉

file-like Object

像open()函数返回的这种有个read()方法的对象,在Python中统称为file-like Object。除了file外,还可以是内存的字节流,网络流,自定义流等等。file-like Object不要求从特定类继承,只要写个read()方法就行。

StringIO就是在内存中创建的file-like Object,常用作临时缓冲。

二进制文件

前面讲的默认都是读取文本文件,并且是ASCII编码的文本文件。要读取二进制文件,比如图片、视频等等,用'rb'模式打开文件即可:

>>> f = open('/Users/michael/test.jpg', 'rb')

>>> f.read()

'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六进制表示的字节

字符编码

要读取非ASCII编码的文本文件,就必须以二进制模式打开,再解码。比如GBK编码的文件:

>>> f = open('/Users/michael/gbk.txt', 'rb')

>>> u = f.read().decode('gbk')

>>> u

u'\u6d4b\u8bd5'

>>> print u

测试

如果每次都这么手动转换编码嫌麻烦(写程序怕麻烦是好事,不怕麻烦就会写出又长又难懂又没法维护的代码),Python还提供了一个codecs模块帮我们在读文件时自动转换编码,直接读出unicode:

import codecs

with codecs.open('/Users/michael/gbk.txt', 'r', 'gbk') as f:

f.read() # u'\u6d4b\u8bd5'

写文件

写文件和读文件是一样的,唯一区别是调用open()函数时,传入标识符'w'或者'wb'表示写文本文件或写二进制文件:

>>> f = open('/Users/michael/test.txt', 'w')

>>> f.write('Hello, world!')

>>> f.close()

你可以反复调用write()来写入文件,但是务必要调用f.close()来关闭文件。当我们写文件时,操作系统往往不会立刻把数据写入磁盘,而是放到内存缓存起来,空闲的时候再慢慢写入。只有调用close()方法时,操作系统才保证把没有写入的数据全部写入磁盘。忘记调用close()的后果是数据可能只写了一部分到磁盘,剩下的丢失了。所以,还是用with语句来得保险:

with open('/Users/michael/test.txt', 'w') as f:

f.write('Hello, world!')

要写入特定编码的文本文件,请效仿codecs的示例,写入unicode,由codecs自动转换成指定编码。

操作文件和目录

#查看当前目录的绝对路径:

>>> os.path.abspath('.')'/Users/michael'

#在某个目录下创建一个新目录,#首先把新目录的完整路径表示出来:

>>> os.path.join('/Users/michael', 'testdir')'/Users/michael/testdir'

#然后创建一个目录:

>>> os.mkdir('/Users/michael/testdir')#删掉一个目录:

>>> os.rmdir('/Users/michael/testdir')

拆分路径:

>>> os.path.split('/Users/michael/testdir/file.txt')

('/Users/michael/testdir', 'file.txt')

过滤文件:

>>> [x for x in os.listdir('.') ifos.path.isdir(x)]

['.lein', '.local', '.m2', '.npm', '.ssh', '.Trash', '.vim', 'Adlm', 'Applications', 'Desktop', ...]

要列出所有的.py文件,也只需一行代码:

>>> [x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py']

['apis.py', 'config.py', 'models.py', 'pymonitor.py', 'test_db.py', 'urls.py', 'wsgiapp.py']

序列化:(pickling)

把变量从内存中变成可存储或传输的过程称之为序列化

把变量从内存中变成可存储或传输的过程称之为反序列化, unpickling

序列化,两个模块,cPickle、pickle,cPickle是C语言写的pickle是Python写的

序列化后就可以写入磁盘,或者网络传输。

>>> d = dict(name='Bob', age=20, score=88)>>> pickle.dumps(d) #把任意一个对象序列化成一个 str,然后,就可以把这个str写入文件。或者用 pickle.dump()

>>> f = open('dump.txt', 'wb')>>> pickle.dump(d, f) #把一个对象序列化后写入一个 file-like Object:

>>> f.close()

当我们要把对象从磁盘读到内存时,可以先把内容读到一个str,然后用pickle.loads()方法反序列化出对象,也可以直接用pickle.load()方法从一个file-like Object中直接反序列化出对象。我们打开另一个Python命令行来反序列化刚才保存的对象:

>>> f = open('dump.txt', 'rb')>>> d =pickle.load(f)>>>f.close()>>>d

{'age': 20, 'score': 88, 'name': 'Bob'}

JSON

JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。

JSON表示的对象就是标准的JavaScript语言的对象

JSON和Python内置的数据类型对应如下:

JSON类型

Python类型

{}

dict

[]

list

"string"

'str'或u'unicode'

1234.56

int或float

true/false

True/False

null

None

dumps()方法还提供了一大堆的可选参数:

将对象转为 JSON

进程和线程

多任务的实现有3种方式:

多进程模式;

多线程模式;

多进程+多线程模式。

多进程(multiprocessing):

Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。

子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。

#Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松创建子进程:

#multiprocessing.py

importosprint 'Process (%s) start...' %os.getpid()

pid=os.fork()if pid==0:print 'I am child process (%s) and my parent is %s.' %(os.getpid(), os.getppid())else:print 'I (%s) just created a child process (%s).' %(os.getpid(), pid)#运行结果如下:

Process (876) start...

I (876) just created a child process (877).

I am child process (877) and my parent is 876.#有了fork调用,一个进程在接到新任务时就可以复制出一个子进程来处理新任务,

常见的Apache服务器就是由父进程监听端口,每当有新的http请求时,

就fork出子进程来处理新的http请求。

multiprocessing

multiprocessing模块提供了一个Process类来代表一个进程对象,

下面的例子演示了启动一个子进程并等待其结束:

from multiprocessing importProcessimportos#子进程要执行的代码

defrun_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, args=('test',))#创建子进程时,只需要传入一个执行函数和函数的参数,

print 'Process will start.'p.start()#创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。

p.join() #join() 方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。

print 'Process end.'

Parent process 928.

Process will start.

Run child process test (929)...

Process end.

Pool

如果要启动大量的子进程,可以用进程池的方式批量创建子进程:

#-*- coding:utf-8 -*-

from multiprocessing importPoolimportos, time, randomdeflong_time_task(name):print 'Run task %s (%s)...' %(name, os.getpid())

start=time.time()

time.sleep(random.random()* 3)

end=time.time()print 'Task %s runs %0.2f seconds.' % (name, (end -start))if __name__=='__main__':print 'Parent process %s.' %os.getpid()

p=Pool()for i in range(5):

p.apply_async(long_time_task, args=(i,))print 'Waiting for all subprocesses done...'p.close()

p.join() //print 'All subprocesses done.'

Parent process 862.

Waitingforall subprocesses done...

Run task 0 (864)...

Run task1 (865)...

Run task2 (866)...

Run task3 (867)...

Task1 runs 0.42seconds.

Run task4 (865)...

Task 0 runs1.41seconds.

Task4 runs 1.65seconds.

Task3 runs 2.42seconds.

Task2 runs 2.64seconds.

All subprocesses done.***Repl Closed***

解读:

对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了。

请注意输出的结果,task 0,1,2,3是立刻执行的,而task 4要等待前面某个task完成后才执行,这是因为Pool的默认大小在我的电脑上是4,因此,最多同时执行4个进程。这是Pool有意设计的限制,并不是操作系统的限制。如果改成:

p = Pool(5)

就可以同时跑5个进程。

由于Pool的默认大小是CPU的核数,如果你不幸拥有8核CPU,你要提交至少9个子进程才能看到上面的等待效果。

多线程

Python的标准库提供了两个模块:thread和threading,thread是低级模块,threading是高级模块,对thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。

启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行:

importtime, threading#新线程执行的代码:

defloop():print 'thread %s is running...' %threading.current_thread().name

n=0while n < 5:

n= n + 1

print 'thread %s >>> %s' %(threading.current_thread().name, n)

time.sleep(1)print 'thread %s ended.' %threading.current_thread().nameprint 'thread %s is running...' %threading.current_thread().name

t= threading.Thread(target = loop, name = 'LoopThread')

t.start()

t.join()#join作用是阻塞主进程(挡住,无法执行join以后的语句

print 'thread %s ended.' % threading.current_thread().name

thread MainThread isrunning...

thread LoopThreadisrunning...

thread LoopThread>>> 1thread LoopThread>>> 2thread LoopThread>>> 3thread LoopThread>>> 4thread LoopThread>>> 5thread LoopThread ended.

thread MainThread ended.***Repl Closed***

join ()方法:

主线程A中,创建了子线程B,并且在主线程A中调用了B.join(),那么,

主线程A会在调用的地方等待,直到子线程B完成操作后,才可以接着往下执行,

那么在调用这个线程时可以使用被调用线程的join方法。

原型join([timeout])里面的参数时可选的,代表线程运行的最大时间,

即如果超过这个时间,不管这个此线程有没有执行完毕都会被回收,

然后主线程或函数都会接着执行的。

由于任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程又可以启动新的线程,

Python的threading模块有个current_thread()函数,它永远返回当前线程的实例。

主线程实例的名字叫MainThread,子线程的名字在创建时指定,我们用LoopThread命名子线程。

名字仅仅在打印时用来显示,完全没有其他意义,

如果不起名字Python就自动给线程命名为Thread-1,Thread-2……

Lock

多线程和多进程最大的不同在于,

多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,

多线程中,所有变量都由所有进程共享,所以,任何一个变量都可以被任何一个线程修改,

因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容改乱了。

锁,当某个线程开始执行change_it()时,我们说,该线程因为获得了锁,

因此其他线程不能同时执行change_it(),只能等待,直到锁被释放后,获得该锁以后才能改。

由于锁只有一个,无论多少线程,同一时刻最多只有一个线程持有该锁,

所以,不会造成修改的冲突。创建一个锁就是通过threading.Lock()来实现:

balance =0

lock=threading.Lock()defrun_thread(n):for i in range(100000):#先要获取锁:

lock.acquire()try:#放心地改吧:

change_it(n)finally:#改完了一定要释放锁:

lock.release()

锁的好处就是确保了某段关键代码只能由一个线程从头到尾完整地执行,坏处当然也很多,首先是阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模 式执行,效率就大大地下降了。其次,由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁,导致多个线程全部挂起,既 不能执行,也无法结束,只能靠操作系统强制终止。

用C、C++或Java来改写相同的死循环,直接可以把全部核心跑满,4核就跑到400%,8核就跑到800%,为什么Python不行呢?

因为Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。

GIL是Python解释器设计的历史遗留问题,通常我们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个不带GIL的解释器。

所以,在Python中,可以使用多线程,但不要指望能有效利用多核。如果一定要通过多线程利用多核,那只能通过C扩展来实现,不过这样就失去了Python简单易用的特点。

不过,也不用过于担心,Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。

ThreadLocal

目的:

在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程,而全局变量的修改必须加锁。

但是局部变量也有问题,就是在函数调用的时候,传递起来很麻烦

#-*- coding:utf-8 -*-

importthreading#创建全局ThreadLocal对象:

local_school =threading.local()defprocess_student():print 'Hello, %s (in %s)' %(local_school.student, threading.current_thread().name)defprocess_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)

全局变量local_school就是一个ThreadLocal对象,每个Thread对它都可以读写student属性,但互不影响。你可以把local_school看成全局变量,但每个属性如local_school.student都是线程的局部变量,可以任意读写而互不干扰,也不用管理锁的问题,ThreadLocal内部会处理。

可以理解为全局变量local_school是一个dict,不但可以用local_school.student,还可以绑定其他变量,如local_school.teacher等等。

ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。

多线程vs多进程

多线程:优点,效率高,但是任何一个线程挂掉都有可能直接造成整个进程奔溃,

因为所有线程共享进程的内存。

多进程:优点,稳定,创建进程的代价大

计算密集型 vs. IO密集型

是否采用多任务的第二个考虑是任务的类型。我们可以把任务分为计算密集型和IO密集型。

对于计算密集型任务,最好用C语言编写。

涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU 和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。

对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言,脚本语言是首选,C语言最差。

现代操作系统对IO操作已经做了巨大的改进,最大的特点就是支持异步IO。如果充分利用操作系统提供的异步IO支持,就可以用单进程单线程模型来执行多任 务,这种全新的模型称为事件驱动模型,Nginx就是支持异步IO的Web服务器,它在单核CPU上采用单进程模型就可以高效地支持多任务。在多核CPU 上,可以运行多个进程(数量与CPU核心数相同),充分利用多核CPU。由于系统总的进程数量十分有限,因此操作系统调度非常高效。用异步IO编程模型来 实现多任务是一个主要的趋势。

分布式进程

#-*- coding:utf-8 -*-

taskmanager.pyimportrandom, time, Queuefrom multiprocessing.managers importBaseManger#发送任务的队列:

task_queue =Queue.Queue()#接受结果的队列:

result_queue =Queue.Queue()#从BaseManager继承的QueueManager:

classQueueManager(BaseManger):pass

#把两个Queue都注册到网络上,callable参数关联了Queue对象:

QueueManager.register('get_task_queue', callable=lambda: task_queue)

QueueManager.register('get_result_queue', callable=lambda: result_queue)#绑定端口5000, 设置验证码'abc':

manager = QueueManager(address=('', 5000), authkey='abc')#启动Queue:

manager.start()

manager.start()#获得通过网络访问的Queue对象:

task =manager.get_task_queue()

result=manager.get_result_queue()#放几个任务进去:

for i in range(10):

n= random.randint(0, 10000)print('Put task %d...' %n)

task.put(n)#从result队列读取结果:

print('Try get results...')for i in range(10):

r= result.get(timeout=10)print('Result: %s' %r)#关闭:

manager.shutdown()

请注意,当我们在一台机器上写多进程程序时,创建的Queue可以直接拿来用,但是,在分布式多进程环境下,添加任务到Queue不可以直接对原始的task_queue进行操作,那样就绕过了QueueManager的封装,必须通过manager.get_task_queue()获得的Queue接口添加。

然后,在另一台机器上启动任务进程(本机上启动也可以):

#taskworker.py

importtime, sys, Queuefrom multiprocessing.managers importBaseManager#创建类似的QueueManager:

classQueueManager(BaseManager):pass

#由于这个QueueManager只从网络上获取Queue,所以注册时只提供名字:

QueueManager.register('get_task_queue')

QueueManager.register('get_result_queue')#连接到服务器,也就是运行taskmanager.py的机器:

server_addr = '127.0.0.1'

print('Connect to server %s...' %server_addr)#端口和验证码注意保持与taskmanager.py设置的完全一致:

m = QueueManager(address=(server_addr, 5000), authkey='abc')#从网络连接:

m.connect()#获取Queue的对象:

task =m.get_task_queue()

result=m.get_result_queue()#从task队列取任务,并把结果写入result队列:

for i in range(10):try:

n= task.get(timeout=1)print('run task %d * %d...' %(n, n))

r= '%d * %d = %d' % (n, n, n*n)

time.sleep(1)

result.put(r)exceptQueue.Empty:print('task queue is empty.')#处理结束:

print('worker exit.')

任务进程要通过网络连接到服务进程,所以要指定服务进程的IP。

现在,可以试试分布式进程的工作效果了。先启动taskmanager.py服务进程:

$ python taskmanager.py

Put task 3411...

Put task 1605...

Put task 1398...

Put task 4729...

Put task 5300...

Put task 7471...

Put task 68...

Put task 4219...

Put task 339...

Put task 7866...

Try get results...

taskmanager进程发送完任务后,开始等待result队列的结果。现在启动taskworker.py进程:

$ python taskworker.py 127.0.0.1

Connect to server 127.0.0.1...

run task 3411 * 3411...

run task 1605 * 1605...

run task 1398 * 1398...

run task 4729 * 4729...

run task 5300 * 5300...

run task 7471 * 7471...

run task 68 * 68...

run task 4219 * 4219...

run task 339 * 339...

run task 7866 * 7866...

worker exit.

taskworker进程结束,在taskmanager进程中会继续打印出结果:

Result: 3411 * 3411 = 11634921

Result: 1605 * 1605 = 2576025

Result: 1398 * 1398 = 1954404

Result: 4729 * 4729 = 22363441

Result: 5300 * 5300 = 28090000

Result: 7471 * 7471 = 55815841

Result: 68 * 68 = 4624

Result: 4219 * 4219 = 17799961

Result: 339 * 339 = 114921

Result: 7866 * 7866 = 61873956

这个简单的Manager/Worker模型有什么用?其实这就是一个简单但真正的分布式计算,把代码稍加改造,启动多个worker,就可以把任务分布到几台甚至几十台机器上,比如把计算n*n的代码换成发送邮件,就实现了邮件队列的异步发送。

Queue对象存储在哪?注意到taskworker.py中根本没有创建Queue的代码,所以,Queue对象存储在taskmanager.py进程中:

而Queue之所以能通过网络访问,就是通过QueueManager实现的。由于QueueManager管理的不止一个Queue,所以,要给每个Queue的网络调用接口起个名字,比如get_task_queue。

authkey有什么用?这是为了保证两台机器正常通信,不被其他机器恶意干扰。如果taskworker.py的authkey和taskmanager.py的authkey不一致,肯定连接不上。

正则表达式

[0-9a-zA-Z\_]可以匹配一个数字、字母或者下划线;

[0-9a-zA-Z\_]+可以匹配至少由一个数字、字母或者下划线组成的字符串,比如'a100','0_Z','Py3000'等等;

[a-zA-Z\_][0-9a-zA-Z\_]*可以匹配由字母或下划线开头,后接任意个由一个数字、字母或者下划线组成的字符串,也就是Python合法的变量;

[a-zA-Z\_][0-9a-zA-Z\_]{0, 19}更精确地限制了变量的长度是1-20个字符(前面1个字符+后面最多19个字符)。

A|B可以匹配A或B,所以(P|p)ython可以匹配'Python'或者'python'。

^表示行的开头,^\d表示必须以数字开头。

$表示行的结束,\d$表示必须以数字结束。

你可能注意到了,py也可以匹配'python',

但是加上^py$就变成了整行匹配,就只能匹配'py'了

re模块

Python提供re模块,包含所有正则表达式的功能。

>>> import re

>>> re.match(r'^\d{3}\-\d{3,8}$', '010-12345')

>>> re.match(r'^\d{3}\-\d{3,8}$', '010 12345')

>>>

match()方法判断是否匹配,如果匹配成功,返回一个Match对象,否则返回None。常见的判断方法就是

importre

test= '用户输入的字符串'

if re.match(r'正则表达式',test):print 'ok'

else:print 'failed'

切分字符串

用正则表达式切分字符串比用固定的字符更灵活,请看正常的切分代码:

>>> 'a b c'.split(' ')

['a', 'b', '', '', 'c']

嗯,无法识别连续的空格,用正则表达式试试:

>>> re.split(r'\s+', 'a b c')

['a', 'b', 'c']

无论多少个空格都可以正常分割。加入,试试:

>>> re.split(r'[\s\,]+', 'a,b, c d')

['a', 'b', 'c', 'd']

再加入;试试:

>>> re.split(r'[\s\,\;]+', 'a,b;; c d')

['a', 'b', 'c', 'd']

分组(group)

编译

当我们在Python中使用正则表达式时,re模块内部会干两件事情:

编译正则表达式,如果正则表达式的字符串本身不合法,会报错;

用编译后的正则表达式去匹配字符串。

>>> 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')

re_email = re.compile(r'[a-z\.]{3,20})@([a-z]+\.[a-z]{3})')

常用内建模块

batteried included

collection是Python内建的一个集合摸块,提供了许多有用的集合类。

namedtuple

tuple 可以表示不变集合,例如,一个点的二维坐标可以表示为:

>>> p = (1, 2)

nametuple 是一个函数,它用来创建一个自定义的tuple对象

并且规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素。

这样一来,我们用namedtuple可以很方便地定义一种数据类型,它具备tuple的不变性,又可以根据属性来引用,使用十分方便。

>>> from collections importnamedtuple>>> Point = namedtuple('Point', ['x', 'y'])>>> p = Point(1, 2)>>>p.x1

>>>p.y2

deque

list 访问快,但是插入和删除慢,因为List是线性存储。

deque是双向链表,适合用于队列和栈

>>> from collections import deque

>>> q = deque(['a', 'b', 'c'])

>>> q.append('x')

>>> q.appendleft('y')

>>> q

deque(['y', 'a', 'b', 'c', 'x'])

deque 可以实现List的:append(), pop(), appendleft(), popleft()

defultdict

使用dict时,如果引用的Key不存在,就会抛出KeyError。如果希望key不存在时,返回一个默认值,就可以用defaultdict:

>>> from collections import defultdit

>>> dd = defaultdict(lambda: 'N/A')

>>> dd['key1'] = 'abc'

>>> dd['key1']

'abc'

>>> dd['key2']

'N/A'

OrderedDict

>>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])

>>> od # OrderedDict的Key是有序的

OrderedDict([('a', 1), ('b', 2), ('c', 3)])

Counter

Counter是一个简单的计数器,例如,统计字符出现的个数:

>>> 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})

Counter实际上也是dict的一个子类,上面的结果可以看出,字符'g'、'm'、'r'各出现了两次,其他字符各出现了一次。

Base64

base64是一种用64 个字符来表示任意二进制数据的方法。

Base64编码会把3字节的二进制数据编码为4字节的文本数据,长度增加33%,好处是编码后的文本数据可以在邮件正文、网页等直接显示。

对二进制数据进行处理,每3个字节一组,一共是3x8=24bit,划为4组,每组正好6个bit

如果要编码的二进制数据不是3的倍数,最后会剩下1个或2个字节怎么办?Base64用\x00字节在末尾补足后,再在编码的末尾加上1个或2个=号,表示补了多少字节,解码的时候,会自动去掉。

由于=字符也可能出现在Base64编码中,但=用在URL、Cookie里面会造成歧义,所以,很多Base64编码后会把=去掉

自动去掉=的:

def b64decode_self(str):

return base64.b64decode(str+'='*(4-len(str)%4))

struct

在Python中,比方说要把一个32位无符号整数变成字节,也就是4个长度的str

1bit:表示1比特

1byte:表示1字节

1byte=8bit

>>> importstruct>>> struct.pack('>I', 10240099)'\x00\x9c@c'pack的第一个参数是处理指令,'>I'的意思是:>表示字节顺序是big-endian,也就是网络序,I表示4字节无符号整数。

后面的参数个数要和处理指令一致。

unpack把str变成相应的数据类型:>>> struct.unpack('>IH', '\xf0\xf0\xf0\xf0\x80\x80')

(4042322160, 32896)

Windows的位图文件(.bmp)是一种非常简单的文件格式,我们来用struct分析一下。

首先找一个bmp文件,没有的话用“画图”画一个。

读入前30个字节来分析:

>>> s = '\x42\x4d\x38\x8c\x0a\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00\x00\x00\x80\x02\x00\x00\x68\x01\x00\x00\x01\x00\x18\x00'

BMP格式采用小端方式存储数据,文件头的结构按顺序如下:

两个字节:'BM'表示Windows位图,'BA'表示OS/2位图; 一个4字节整数:表示位图大小; 一个4字节整数:保留位,始终为0; 一个4字节整数:实际图像的偏移量; 一个4字节整数:Header的字节数; 一个4字节整数:图像宽度; 一个4字节整数:图像高度; 一个2字节整数:始终为1; 一个2字节整数:颜色数。

所以,组合起来用unpack读取:

>>> struct.unpack('

('B', 'M', 691256, 0, 54, 40, 640, 360, 1, 24)

结果显示,'B'、'M'说明是Windows位图,位图大小为640x360,颜色数为24。

请编写一个bmpinfo.py,可以检查任意文件是否是位图文件,如果是,打印出图片大小和颜色数。

importstructdeftest(img_dir):

with open(img_dir,'rb')as f:

tu= struct.unpack('

IIHH', f.read(30))if tu[0] == 'B' and tu[1] == 'M':print 'windows bitmap,size:%s, width:%s,\

height:%s' %(tu[2],tu[6], tu[7])elif tu[0] == 'B' and tu[1] == 'A':print 'OS\2:, width:%s, height:%s' %(tu[6], tu[7])else:print 'sorry'f.close()

test('/Users/Y1Y/Desktop/1.bmp')

socket

# 导入socket库:

import socket

# 创建一个socket:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 建立连接:

s.connect(('www.sina.com.cn', 80))

创建Socket时,AF_INET指定使用IPv4协议,如果要用更先进的IPv6,就指定为AF_INET6。SOCK_STREAM指定使用面向流的TCP协议,这样,一个Socket对象就创建成功,但是还没有建立连接。

客户端要主动发起TCP连接,必须知道服务器的IP地址和端口号。新浪网站的IP地址可以用域名www.sina.com.cn自动转换到IP地址,但是怎么知道新浪服务器的端口号呢?

答案是作为服务器,提供什么样的服务,端口号就必须固定下来。由于我们想要访问网页,因此新浪提供网页服务的服务器必须把端口号固定在80端口,因为80端口是Web服务的标准端口。其他服务都有对应的标准端口号,例如SMTP服务是25端口,FTP服务是21端口,等等。端口号小于1024的是Internet标准服务的端口,端口号大于1024的,可以任意使用。

因此,我们连接新浪服务器的代码如下:

s.connect(('www.sina.com.cn', 80))

注意参数是一个tuple,包含地址和端口号。

建立TCP连接后,我们就可以向新浪服务器发送请求,要求返回首页的内容:

# 发送数据:

s.send('GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值