Python学习第四周总结
正则表达式
是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。在很多文本编辑器或其他工具里,正则表达式通常被用来检索和/或替换那些符合某个模式的文本内容。许多程序设计语言都支持利用正则表达式进行字符串操作。Python提供了re模块来支持正则表达式相关操作。
一些常用的元字符
符号 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线或汉字 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\b | 匹配单词的开始或结束 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
{N} | 匹配N次 |
{M,} | 匹配至少M次 |
{M,N} | 匹配至少M次最多N次 |
{M,N}? | 匹配M到N次,但尽可能少匹配 |
{M,}? | 匹配至少M次但尽可能少匹配 |
* | 匹配零次或多次 |
+ | 匹配一次或多次 |
? | 匹配零次或一次 |
Python中re模块的一些常用函数
函数 | 说明 |
---|---|
ompile(pattern, flags=0) | 编译正则表达式返回正则表达式对象 |
match(pattern, string, flags=0) | 用正则表达式匹配字符串 成功返回匹配对象 否则返回None(从头开始匹配) |
search(pattern, string, flags=0) | 搜索字符串中第一次出现正则表达式的模式 成功返回匹配对象 否则返回None(从任意四方开始匹配) |
findall(pattern, string, flags=0) | 匹配字符串所有与正则表达式匹配的模式 返回字符串的列表 |
split(pattern, string, maxsplit=0, flags=0) | 用正则表达式指定的模式分隔符拆分字符串 返回列表 |
sub(pattern, repl, string, count=0, flags=0) | 用指定的字符串替换原字符串中与正则表达式匹配的模式 可以用count指定替换的次数 |
下面是几个例子
# 电信 133 153 180 181 189 177
# 联通 130 131 132 155 156 185 186 145 176
# 移动 134 135 136 137 138 139 150 151 152 157 158 159 182 183 184 187 188 147 178
# 输入手机号判断是哪个运营商的手机号
import re
def main():
phone_num = input('请输入要查询的手机号码:')
dx = re.compile(r'^1(33|53|80|81|89|77|99)\d{8}$')
lt = re.compile(r'^1(3[0-2]|55|56|85|86|45|76)\d{8}$')
yd = re.compile(r'^1(3[4-9]|5[0-2]|5[7-9]|8[2-4]|87|88|47|78)\d{8}$')
if dx.match(phone_num):
print('此为电信手机号。')
elif lt.match(phone_num):
print('此为联通手机号。')
elif yd.match(phone_num):
print('此为移动手机号。')
else:
print('此为无效的手机号码!')
if __name__ == '__main__':
main()
# 替换字符串中的屏蔽词汇
import re
def main():
sentence = '你丫是傻叉吗? 我操你大爷的. Fuck you.'
purified = re.sub('[操肏艹草曹]|fuck|shit|傻[比屄逼叉缺吊屌]|煞笔',
'*', sentence, flags=re.IGNORECASE)
print(purified) # 你丫是*吗? 我*你大爷的. * you.
if __name__ == '__main__':
main()
在Python中,正则表达式多应用于爬虫,如果以后想走爬虫方向的话,那么正则表达式一定是需要多多练习的,如果有不熟的地方可以去看一下《正则表达式30分钟入门教程》
进程与线程
进程
进程是操作系统的分配内存的基本单位,进程之间的内存是相互隔离的
如果进程之间想要通信就要通过IPC机制和socket套接字。
进程可以划分为线程, 线程是进程的执行单元,也是操作系统分配cpu的基本单元
如果一个任务执行时间很长,就将程序分为多个线程,提升执行效率,缩短程序的执行时间
让用户获得更好的用户体验
process 进程
常用方法
创建线程的两种方式
直接创建Thread对象, 并通过target参数指定线程启动后要执行的任务
通过继承Thread自定义线程类重写run方法指定线程启动后执行的任务
方法 | 解释 |
---|---|
p.daemon = True | 守护进程,守护进程不可以再有子进程,并且主进程死守护进程就死,要写在p.start()之前 |
p.join() | 主进程等子进程执行完 之后再结束—> 等的时间就是执行时间最长的子进程执行的时间 |
p.terminate()- | 强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,用该方法需要特别小心这种情况。如果p保存了一个锁那么也将不会被释放,进而导致死锁 |
p.is_alive() | 如果p仍然运行,返回True |
p.name | 查看进程的名称 |
p.pid | 查看进程的pid |
关于lock的用法
def deposit(self, money):
# 但多个线程同时访问一个资源的时候,就有可能因为竞争资源导致资源的状态错误
# 被多个线程访问的资源我们通常称之为临界资源,对临界资源的访问需要加以保护
if money > 0:
# 抢到锁的线程进行对资源的操作
self._lock.acquire()
try:
new_balance = self._balance + money
time.sleep(0.01)
self._balance = new_balance
finally:
self._lock.release()
线程
创建线程的两种方式
直接创建Thread对象, 并通过target参数指定线程启动后要执行的任务
通过继承Thread自定义线程类重写run方法指定线程启动后执行的任务
如果多个任务之间没有任何关联(独立子任务)而且希望能利用CPU的多核特性
那么我们推荐使用多进程
常用方法
函数 | 说明 |
---|---|
isAlive() | 返回线程是否在运行。正在运行指启动后、终止前 |
get/setName(name) | 获取/设置线程名 |
start() | 线程准备就绪,等待CPU调度 |
is/setDaemon(bool) | 获取/设置是后台线程(默认前台线程(False)):如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,主线程和后台线程均停止。如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程也执行完成后,程序停止 |
start(): | 启动线程 |
join([timeout]) | 阻塞当前上下文环境的线程,直到调用此方法的线程终止或到达指定的timeout(可选参数) |
自定义线程类
创建线程的两种方式
直接创建Thread对象, 并通过target参数指定线程启动后要执行的任务
通过继承Thread自定义线程类重写run方法指定线程启动后执行的任务
通常对于多线程的任务我们推荐的是使用自定义线程类
class My_thread(Thread):
def __init__(self, str1, count):
super().__init__()
self._str1 = str1
self._count = count
def run(self):
for _ in range(self._count):
print(self._str1, end=' ', flush=True)
socket
通过socket套接字进行网路数据的传输
TCP
TCP提供一种面向连接的、可靠的字节流服务。面向连接意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据包之前必须先建立一个TCP连接。这一过程与打电话很相似,先拨号振铃,等待对方摘机说“喂”,然后才说明是谁。在一个TCP连接中,仅有两方进行彼此通信。广播和多播不能用于TCP
server端
def main():
# 创建一个基于TCP的套接字对象
# 因为我们做的可能是一个应用级的产品或服务,所以可以利用一个现有的传输服务来实现数据传输
server = socket(AF_INET, SOCK_STREAM)
# 绑定IP地址 端口号:用来区分同一个地址不同的服务 0-65535
# 端口HTTP-80 HTTPS-443 SMTP-25 POP3-110 选择端口避开1024前
server.bind(('10.7.189.82', 2048))
# server.bind(('localhost', 2048))
# server.bind(('127.0.0.1', 2048))
# 监听 参数:队列大小(合适就好)
server.listen(512)
print('server start listen...')
# netstat -na查看正在执行服务端口
while True:
# 通过accept方法接收客户端的连接
# accept方法是一个阻塞式的方式 如果没有客户端连接
# 那么accept方法就会让代码阻塞,直到客户端连接才返回
# accept方法返回一个元组,元组中的第一个值代表的是客户端对象
# 元组中的第二个值又是一个元组, 其中有客户端的IP地址和客户端的端口
# telnet ip 端口 连接服务器
client, addr = server.accept()
curr_time = datetime.datetime.now()
client.send(curr_time.__str__().encode('utf-8'))
print(addr, 'connect success')
socket.close()
client端
client = socket()
client.connect(('10.7.189.82', 2048))
my_time = client.recv(1024)
print(type(my_time))
print(my_time.decode('utf-8'))
UDP
在选择使用协议的时候,选择UDP必须要谨慎。在网络质量令人十分不满意的环境下,UDP协议数据包丢失会比较严重。但是由于UDP的特性:它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。比如我们聊天用的ICQ和QQ就是使用的UDP协议。
通过UDP传输图片例子
发送端
from socket import socket, SOCK_DGRAM
import time
def main():
sender = socket(type=SOCK_DGRAM)
with open('6597532661052146017.jpg', 'rb') as img:
data = img.read()
data_len = len(data)
total = 0
while total < data_len:
sender.sendto(data[total:total+1024],
(('10.7.189.82', 9876)))
total += 1024
time.sleep(0.001)
print('over')
接收端
from socket import socket, SOCK_DGRAM
import time
def main():from socket import socket, SOCK_DGRAM
def main():
reciver = socket(type=SOCK_DGRAM)
reciver.bind(('10.7.189.82', 9876))
data = bytes()
while True:
seg, addr = reciver.recvfrom(1024)
data += seg
if len(data) == 174355:
break
with open('hello.jpg', 'wb')as f:
f.write(data)
print('图片已接收')