您的位置:首页 > 编程语言 > Python开发

Python 基础回顾(八)

2016-09-25 22:23 267 查看
clsss queue(object):

def init(self):

self.queue = list()

def pop(self):

self.queue.pop(0)

def push(self, item):

self.queue.append(item)

def isempty(self):

return len(self.queue) == 0

def top(self):

if self.isempty():

return None

return self.queue[0]

re模块

在Python中使用正则表达式是非常方便的。Python提供了re模块,包含所有正则表达式的功能。由于Python的字符串本身也用\转义,所以要特别注意:

s = ‘ABC\-001’ # Python的字符串

对应的正则表达式字符串变成:

‘ABC-001’

因此我们强烈建议使用Python的r前缀,就不用考虑转义的问题了:

s = r’ABC-001’ # Python的字符串

对应的正则表达式字符串不变:

‘ABC-001’

先看看如何判断正则表达式是否匹配:

import re

re.match(r’^\d{3}-\d{8,8}′,‘010−62251234′)<sre.SREMatchobjectat0x1026e18b8>re.match(r′\d3−\d8,8’, ‘010 62251234’)

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

astr = ‘用户输入的字符串’

if re.match(r’正则表达式’, astr):

print ‘ok’

else:

print ‘(>﹏<=’

切分字符串

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

‘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)。比如:

^(\d{3})-(\d{8,8})分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码:m=re.match(r′(\d3)−(\d8,8)’, ‘010-62281234’)

m

<_sre.SRE_Match object at 0x1026fb3e8>

m.group(0)

‘010-62281234’

m.group(1)

‘010’

m.group(2)

‘62281234’

如果正则表达式中定义了组,就可以在Match对象上用group()方法提取出子串来。

注意到group(0)永远是原始字符串,group(1)、group(2)……表示第1、2、……个子串。

提取子串非常有用。

print re.search(‘^From’, ‘From Here to Eternity’)

<_sre.SRE_Match object at 0x…>

print re.search(‘^From’, ‘Reciting From Memory’)

None

贪婪匹配

最后需要特别指出的是,正则匹配默认是贪婪匹配,也就是匹配尽可能多的字符。举例如下,匹配出数字后面的0:

re.match(r’^(\d+)(0*)′,‘102300′).groups()(‘102300′,”)由于\d+采用贪婪匹配,直接把后面的0全部匹配了,结果0∗只能匹配空字符串了。必须让\d+采用非贪婪匹配(也就是尽可能少匹配),才能把后面的0匹配出来,加个?就可以让\d+采用非贪婪匹配:re.match(r′(\d+?)(0∗)’, ‘102300’).groups()

(‘1023’, ‘00’)

编译

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

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

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

如果一个正则表达式要重复使用几千次,出于效率的考虑,我们可以预编译该正则表达式,接下来重复使用时就不需要编译这个步骤了,直接匹配:

import re

编译:

re_telephone = re.compile(r’^(\d{3})-(\d{8,8})$’)

使用:

re_telephone.match(‘010-62281234’).groups()

(‘010’, ‘62281234’)

re_telephone.match(‘010-11115200’).groups()

(‘010’, ‘11115200’)

编译后生成Regular Expression对象,由于该对象自己包含了正则表达式,所以调用对应的方法时不用给出正则字符串。

import re

\d [0-9].

\D [^0-9].

\s [ \t\n\r\f\v].

\S [^ \t\n\r\f\v].

\w [a-zA-Z0-9_].

\W [^a-zA-Z0-9_].

小结

正则表达式非常强大,内容特别多。要讲清楚正则的所有内容,可以写一本厚厚的书了。如果你经常遇到正则表达式的问题,你可能需要一本正则表达式的参考书。 如果想深入了解,就去看这本书吧。http://regexper.com/

来作业啦: 使用requests库抓取一个页面。 把其中的图片URL用正则表达式分析出来。(^__^)

进程和线程

面试常见问题: 什么是进程和线程?说说进程和线程的区别?

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一些必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源

一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.

现在,多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务。由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢?

答案就是操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。



我们前面编写的所有的Python程序,都是执行单任务的进程,也就是只有一个线程。如果我们要同时执行多个任务怎么办?还记得那个服务器程序吗?

有两种解决方案:

一种是启动多个进程,每个进程虽然只有一个线程,但多个进程可以一块执行多个任务。

还有一种方法是启动一个进程,在一个进程内启动多个线程,这样,多个线程也可以一块执行多个任务。

第三种方法,就是启动多个进程,每个进程再启动多个线程。这种模型更复杂,实际很少采用。

总结一下就是,多任务的实现有3种方式:

多进程模式;

多线程模式;

多进程+多线程模式。

同时执行多个任务通常各个任务之间并不是没有关联的,而是需要相互通信和协调,有时,任务1必须暂停等待任务2完成后才能继续执行,有时,任务3和任务4又不能同时执行,所以,多进程和多线程的程序的复杂度要远远高于我们前面写的单进程单线程的程序。

因为复杂度高,调试困难,所以,不是迫不得已,我们也不想编写多任务。但是,有很多时候,没有多任务还真不行。想想在电脑上看电影,就必须由一个线程播放视频,另一个线程播放音频,否则,单线程实现的话就只能先把视频播放完再播放音频,或者先把音频播放完再播放视频,这显然是不行的。

Python既支持多进程,又支持多线程。 先看看多进程。

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

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

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

multiprocessing.py

import os

print ‘Process (%s) start…’ % os.getpid()

p = os.fork()

print p

if p==0:

print ‘child process (%s) and my parent is %s.’ % (os.getpid(), os.getppid())

while(1)

print ‘hello’

else:

print ‘I (%s) just created a child process (%s).’ % (os.getpid(), p)

print ‘hello’

运行结果如下:

Process (876) start…

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

I am child process (877) and my parent is 876.

由于Windows没有fork调用,上面的代码在Windows上无法运行。lxf你还行吗?

有了fork调用,一个进程在接到新任务时就可以复制出一个子进程来处理新任务,常见的Apache服务器就是由父进程监听端口,每当有新的http请求时,就fork出子进程来处理新的http请求。

multiprocessing

由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块就是跨平台版本的多进程模块。

multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束:

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, args=(‘test’,))

print ‘Process will start.’

p.start()

p.join()

print ‘Process end.’

执行结果如下:

Parent process 928.

Process will start.

Run child process test (929)…

Process end.

创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。

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

Pool

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

from multiprocessing import Pool

import os, time, random

def long_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 28970.

Waiting for all subprocesses done…

Run task 0 (28971)…

Task 0 runs 1.06 seconds.

Run task 1 (28971)…

Task 1 runs 0.36 seconds.

Run task 2 (28971)…

Task 2 runs 0.13 seconds.

Run task 3 (28971)…

Task 3 runs 0.35 seconds.

Run task 4 (28971)…

Task 4 runs 0.06 seconds.

print ‘All subprocesses done.’

代码解读:

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

请注意输出的结果,执行顺序有啥不同吗。我们试试改动一下Pool的参数。 如果改成:

p = Pool(0)

p = Pool(1)

p = Pool(2)

p = Pool(3)

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

进程间通信

常见面试问题: 进程间通信方式有哪些?

无名管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

高级管道(popen):将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的子进程,这种方式我们成为高级管道方式。

3. 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

4. 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

5. 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

6. 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

7. 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。

8. 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

小结: 线程是最小的执行单元,而进程由至少一个线程组成。如何调度进程和线程,完全由操作系统决定,程序自己不能决定什么时候执行,执行多长时间。

多进程和多线程的程序涉及到同步、数据共享的问题,编写起来很难很复杂。

注意啦,注意啦!! 我们的实战大作业要来了。会议管理系统

满足功能点:

1. 用户注册,登录

2. 发布会议( 有可能有很多种会议哦)

3. 用户报名参加某个会议,知道同行的人有哪些?看看有没有认识的人?(社交功能)

4. 用户评论。

使用数据库

数据库(database,DB)是指长期存储在计算机内的,有组织,可共享的数据的集合。数据库中的数据按一定的数学模型组织、描述和存储,具有较小的冗余,较高的数据独立性和易扩展性,并可为各种用户共享。 现在流行的数据库类型有关系型的非关系型的。好像是废话?

常见数据库:Oracle MySQL Redis SQL server ….

我们主要关注的是MySQL和Redis。 数据库安装就交给你们啦。

先说结论。我们先来装一个Python库吧 pip install SQLAlchemy ,这就是传说中的ORM。ORM框架的作用就是把数据库表的记录和Python中的数据做了一个关系映射。Python中一切皆是对象,数据库中一切都是记录。有了ORM,这个世界又和谐了。

我们得先了解一下这个SQLAlchemy

http://docs.sqlalchemy.org/en/rel_1_0/ 不错,又是一场恶战
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python pop 回顾 对象