八、第三方库
主要是
通过pip的方式安装:
pip3 install PackageName
python3 -m pip install PackageName
sudo pip install PackageName
pip install --upgrade pip #升级pip
pip uninstall flask #卸载库
pip list #查看已安装库
九、文件读写
读文件:read
file = open('url.txt','r')
调用read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- try: f = open('/Users/XXX/Desktop/url.txt','r'encoding='utf-8', errors='ignore') print(f.read()) finally: if f: f.close()
最后一步是调用close()方法关闭文件
但是每次都需要关闭文件流:(上面的文件可以改写成)
#!/usr/bin/env python3 # -*- coding: utf-8 -*- with open('/Users/fuwei/Desktop/url.txt','r', encoding='utf-8', errors='ignore') as file: print(file.read())
encoding='utf-8', errors='ignore'
中文编码和忽略非法编码的字符
写文件:write
file = open('url.txt','w')
调用read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- try: f = open('/Users/XXX/Desktop/url.txt','w'encoding='utf-8', errors='ignore') print(f.read()) finally: if f: f.close()
最后一步是调用close()方法关闭文件
但是每次都需要关闭文件流:(上面的文件可以改写成)
#!/usr/bin/env python3 # -*- coding: utf-8 -*- with open('/Users/fuwei/Desktop/url.txt','w', encoding='utf-8', errors='ignore') as file: print(file.read())
encoding='utf-8', errors='ignore'
中文编码和忽略非法编码的字符
会发现,以'w'模式写入文件时,如果文件已存在,会直接覆盖(相当于删掉后新写入一个文件)。如果我们希望追加到文件末尾怎么办?可以传入'a'以追加(append)模式写入。
StringIO和BytesIO[字符流和字节流]
StringIO和BytesIO是在内存中操作str和bytes的方法,使得和读写文件具有一致的接口。
操作文件和目录:
要操作文件、目录,可以在命令行下面输入操作系统提供的各种命令来完成。比如dir、cp等命令。
操作文件和目录
# 查看当前目录的绝对路径: >>> os.path.abspath('.') '/Users/michael' # 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来: >>> os.path.join('/Users/michael', 'testdir') '/Users/michael/testdir' # 然后创建一个目录: >>> os.mkdir('/Users/michael/testdir') # 删掉一个目录: >>> os.rmdir('/Users/michael/testdir')
Python的os模块封装了操作系统的目录和文件操作,要注意这些函数有的在os模块中,有的在os.path模块中。
十、进程和线程
提高效率:
一种是启动多个进程,每个进程虽然只有一个线程,但多个进程可以一块执行多个任务。
还有一种方法是启动一个进程,在一个进程内启动多个线程,这样,多个线程也可以一块执行多个任务。
当然还有第三种方法,就是启动多个进程,每个进程再启动多个线程,这样同时执行的任务就更多了,当然这种模型更复杂,实际很少采用。
总结一下就是,多任务的实现有3种方式:
多进程模式;
多线程模式;
多进程+多线程模式。
多进程:process
fork(Unix/Linux/Mac)
Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os def process(): print('Process (%s) start...' % os.getpid()) # Only works on Unix/Linux/Mac: pid = os.fork() #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)) if __name__ == '__main__': process()
由于Windows没有fork调用,fork只能在Unix/Linux/Mac
multiprocessing(多平台 Unix/Linux/Mac/win)
由于Windows没有fork调用,难道在Windows上无法用Python编写多进程的程序?
由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块就是跨平台版本的多进程模块。
multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from multiprocessing import Process import os # 子进程要执行方法 def run_proc(name): print('运行子进程 %s (%s)...' % (name, os.getpid())) if __name__=='__main__': print('父进程 %s.' % os.getpid()) #打印当前的进程 p = Process(target=run_proc, args=('test',)) #调用指定方法run_proc 传入参数 print('子进程将运行.') p.start() #启动子进程 p.join() #将进程添加到调用的进程 print('子进程关闭')
Pool (进程池的方式批量创建子进程)
如果要启动大量的子进程,可以用进程池的方式批量创建子进程:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from multiprocessing import Pool import os, time, random # 子进程要执行方法 def run_proc(name): print('运行子进程 %s (%s)...' % (name, os.getpid())) start = time.time() time.sleep(random.random() * 3) end = time.time() print('运行子进程 %s 运行的时间 %2f' % (name,(end - start))) #计算子进程运行的时间 if __name__=='__main__': print('父进程 %s.' % os.getpid()) #打印当前的进程 p = Pool(4) #创建进程连接池-最大的连接数是4 for i in range(5): p.apply_async(run_proc, args = (i , )) #连接池调用运行进程的方法,同时传入参数 print('等待全部的进程完成。。。') p.close() #关闭连接池 p.join() #调用全部的子进程执行 print('全部的进程完成')
添加详细的每一步的注释。
代码解读:
对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了。
子进程:
很多时候,子进程并不是自身,而是一个外部进程。我们创建了子进程后,还需要控制子进程的输入和输出。
subprocess模块可以让我们非常方便地启动一个子进程,然后控制其输入和输出。
下面的例子演示了如何在Python代码中运行命令nslookup www.python.org,这和命令行直接运行的效果是一样的: #!/usr/bin/env python3 # -*- coding: utf-8 -*- import subprocess def lookup(): print('$ nslookup www.python.org') r = subprocess.call(['nslookup', 'www.python.org']) print('Exit code:', r) if __name__=='__main__': lookup()
运行的结果:
上面的代码相当于nslookup,利用DNS去解析
如何使用指定DNS服务器查询?
语法为 nslookup -qt=类型 目标域名 指定的DNS服务器IP或域名
例子:nslookup -qt=A tool.chinaz.com 8.8.8.8
如果子进程还需要输入,则可以通过communicate()方法输入:
import subprocess print('$ nslookup') p = subprocess.Popen(['nslookup'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, err = p.communicate(b'set q=mx\npython.org\nexit\n') print(output.decode('utf-8')) print('Exit code:', p.returncode)
进程间通信:
Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from multiprocessing import Process, Queue import os, time, random # 写数据进程执行的代码: def write(q): print('写入write进程 : %s ' % os.getpid()) #写入到队列的进程 for vlaue in ['A', 'B', 'C']: print('传入 %s to 队列(queue)' % vlaue) q.put(vlaue) #调用put方法写入 time.sleep(random.random()) #去读数据进程执行的代码 def read(q): print('读取read进程 : %s ' % os.getpid()) while True: vlaue = q.get(True) print('获取get %s 来自队列' % vlaue) if __name__=='__main__': # 父进程创建Queue,并且传给给个子进程: q = Queue() pw = Process(target=write, args=(q,)) pr = Process(target=read, args=(q,)) #启动子进程pw,写入: pw.start() #启动子进程pr,读取: pr.start() #等待PW结束: pw.join() #pr读取进程是死循环,无法等待其结束,职能强行终止: pr.terminate()
运行结果:
在Unix/Linux下,multiprocessing模块封装了fork()调用,使我们不需要关注fork()的细节。由于Windows没有fork调用,因此,multiprocessing需要“模拟”出fork的效果,父进程所有Python对象都必须通过pickle序列化再传到子进程去,所以,如果multiprocessing在Windows下调用失败了,要先考虑是不是pickle失败了。
小结
在Unix/Linux下,可以使用fork()调用实现多进程。
要实现跨平台的多进程,可以使用multiprocessing模块。
进程间通信是通过Queue、Pipes等实现的。
多线程:Thread
Python的标准库提供了两个模块:_thread和threading,_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数情况下,我们只需要使用threading这个高级模块。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import time, threading #新线程执行代码 def loop(): print('thread is %s runing' % 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 is end ' % threading.current_thread().name) print('thread %s is runing ..' % threading.current_thread().name) t = threading.Thread(target = loop, name = 'LoopThread') t.start() t.join() print('thread %s ended' % threading.current_thread().name)#打印结束当前主的线程名字
由于任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程又可以启动新的线程,Python的threading模块有个current_thread()函数,它永远返回当前线程的实例。主线程实例的名字叫MainThread,子线程的名字在创建时指定,我们用LoopThread命名子线程。名字仅仅在打印时用来显示,完全没有其他意义,如果不起名字Python就自动给线程命名为Thread-1,Thread-2……
Lock
当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。
balance = 0 lock = threading.Lock() def run_thread(n): for i in range(100000): # 先要获取锁: lock.acquire() try: # 放心地改吧: change_it(n) finally: # 改完了一定要释放锁: lock.release()
ThreadLocal:
在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程,而全局变量的修改必须加锁。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import threading #创建全局的threadloacl对象: 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()
不同的线程绑定自己线程
全局变量local_school就是一个ThreadLocal对象,每个Thread对它都可以读写student属性,但互不影响。你可以把local_school看成全局变量,但每个属性如local_school.student都是线程的局部变量,可以任意读写而互不干扰,也不用管理锁的问题,ThreadLocal内部会处理。
可以理解为全局变量local_school是一个dict,不但可以用local_school.student,还可以绑定其他变量,如local_school.teacher等等。
ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。
小结:
一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。ThreadLocal解决了参数在一个线程中各个函数之间互相传递的问题。
正则表达式:
表1.常用的元字符
代码
说明
.
匹配除换行符以外的任意字符
\w
匹配字母或数字或下划线或汉字
\s
匹配任意的空白符
\d
匹配数字
\b
匹配单词的开始或结束
^
匹配字符串的开始
$
匹配字符串的结束
例子:
\d{3}表示匹配3个数字,例如'010';
\s可以匹配一个空格(也包括Tab等空白符),所以\s+表示至少有一个空格,例如匹配' ',' '等;
\d{3,8}表示3-8个数字,例如'1234567'。
进阶
要做更精确地匹配,可以用[]表示范围,比如:
[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.match() 方法
match()方法判断是否匹配,如果匹配成功,返回一个Match对象,否则返回None。常见的判断方法就是:
test = '用户输入的字符串' if re.match(r'正则表达式', test): print('ok') else: print('failed')
表2.常用的限定符
重复
代码/语法
说明
*
重复零次或更多次
+
重复一次或更多次
?
重复零次或一次
{n}
重复n次
{n,}
重复n次或更多次
{n,m}
重复n到m次
分组:
除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用()表示的就是要提取的分组(Group)。比如:
^(\d{3})-(\d{3,8})$分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码:
>>> import re >>> m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345') >>> m.group(0) '010-12345' >>> m.group(1) '010' >>> m.group(2) '12345'
网友评论