面向对象继承
多重继承
如果一个类有多个父类, 而多个父类又有公共的父类(菱形继承, 钻石继承)
那么在搜索属性和方法时搜索的一局是C3算方法(有点类似于广度优先搜索)
这个是Python中的一个改进, 在此之前搜索的算法是深度优先算法
class A(object):
def foo(self):
print('A\'s foo')
class B(A):
def foo(self):
print('B\'s foo')
class C(A):
def foo(self):
print('C\'s foo')
class D(B, C):
def foo(self):
pass
尽量让后两个父类设定为抽象类, 在子类中实现方法
class Father(object):
def __init__(self, name):
self._name = name
def drink(self):
print(self._name, 'is drinking')
def gamble(self):
print(self._name, 'is gamble')
class Monk(object, metaclass=ABCMeta):
@abstractmethod
def eat_vegetable(self):
pass
@abstractmethod
def chant(self):
pass
class Musician(object, metaclass=ABCMeta):
@abstractmethod
def play_piano(self):
pass
@abstractmethod
def drink(self):
pass
class Son(Father, Monk, Musician):
def __init__(self, name, nickname, art_name):
# 给类传参数需要传self
Father.__init__(self, name)
self._nickname = nickname
self._art_name = art_name
def eat_vegetable(self):
print(self._nickname, 'is eating vegetable')
def chant(self):
print(self._nickname, 'is chanting')
def play_piano(self):
print(self._art_name, 'is playing piano')
def drink(self):
print('master', self._art_name, 'is drinking')
正则表达式
基本符号
符号 | 解释 | 示例 | 说明 |
---|---|---|---|
匹配任意字符 | b.t | 示例 | 可以匹配bat / but / b#t / b1t等 |
\w | 匹配字母/数字/下划线 | b\wt | 可以匹配bat / b1t / b_t等 但不能匹配b#t |
\s | 匹配空白字符(包括\r、\n、\t等) | love\syou | 可以匹配love you |
\d | 匹配数字 | \d\d | 可以匹配01 / 23 / 99等 |
\b | 匹配单词的边界 | \bThe\b | |
^ | 匹配字符串的开始 | ^The | 可以匹配The开头的字符串 |
匹配字符串的结束 | .exe | 可以匹配.exe结尾的字符串 | |
\W | 匹配非字母/数字/下划线 | b\Wt | 可以匹配b#t / b@t等,但不能匹配but / b1t / b_t等 |
\S | 匹配非空白字符 | love\Syou | 可以匹配love#you等,但不能匹配love you |
\D | 匹配非数字 | \d\D | 可以匹配9a / 3# / 0F等 |
\B | 匹配非单词边界 | \Bio\B | |
[] | 匹配来自字符集的任意单一字符 | [aeiou] | 可以匹配任一元音字母字符 |
[^] | 匹配不在字符集中的任意单一字符 | [^aeiou] | 可以匹配任一非元音字母字符 |
* | 匹配0次或多次 | \w* | |
+ | 匹配1次或多次 | \w+ | |
? | 匹配0次或1次 | \w? | |
{N} | 匹配N次 | \w{3} | |
{M,} | 匹配至少M次 | \w{3,} | |
{M,N} | 匹配至少M次至多N次 | \w{3,6} | |
/ | 分支 | foo/ | |
(?#) | 注释 | ||
(exp) | 匹配exp并捕获到自动命名的组中 | ||
(?exp) | 匹配exp并捕获到名为name的组中 | ||
(?:exp) | 匹配exp但是不捕获匹配的文本 | ||
(?=exp) | 匹配exp前面的位置 | \b\w+(?=ing) | \b\w+(?=ing) |
(?<=exp) | 匹配exp后面的位置 | (?<=\bdanc)\w+\b | 可以匹配I love dancing and reading中的第一个ing |
(?!exp) | 匹配前面不是exp的位置 | ||
(? | 匹配前面不是exp的位置 | ||
*? | 重复任意次,但尽可能少重复 | 重复任意次,但尽可能少重复 | 将正则表达式应用于aabab,前者会匹配整个字符串aabab,后者会匹配aab和ab两个字符串 |
+? | 重复1次或多次,但尽可能少重复 | ||
?? | 重复1次或多次,但尽可能少重复 | ||
{M,N}? | 重复M到N次,但尽可能少重复 | ||
{M,}? | 重复M次以上,但尽可能少重复 |
re模块核心函数
函数 | 说明 |
---|---|
compile(pattern, flags=0) | 编译正则表达式返回正则表达式对象 |
match(pattern, string, flags=0) | 用正则表达式匹配字符串 成功返回匹配对象 否则返回None |
search(pattern, string, flags=0) | 搜索字符串中第一次出现正则表达式的模式 成功返回匹配对象 否则返回None |
split(pattern, string, maxsplit=0, flags=0) | 用正则表达式指定的模式分隔符拆分字符串 返回列表 |
sub(pattern, repl, string, count=0, flags=0) | 用指定的字符串替换原字符串中与正则表达式匹配的模式 可以用count指定替换的次数 |
fullmatch(pattern, string, flags=0) | match函数的完全匹配(从字符串开头到结尾)版本 |
findall(pattern, string, flags=0) | 查找字符串所有与正则表达式匹配的模式 返回字符串的列表 |
finditer(pattern, string, flags=0) | 查找字符串所有与正则表达式匹配的模式 返回一个迭代器 |
purge() | 清除隐式编译的正则表达式的缓存 |
re.I / re.IGNORECASE | 忽略大小写匹配标记 |
re.M / re.MULTILINE | 多行匹配标记 |
例子
main():
qq = input('QQ:')
username = input('username:')
phone_num = input('phonenumber:')
# 提前准备正则表达式
pattern1 = re.compile(r'^[0-9a-zA-Z_]{6,20}$')
if re.match(r'^[1-9]\d{4,11}$', qq):
print(re.match(r'^[1-9]\d{4,11}$', qq))
print('QQ success')
else:
print('QQ error')
if pattern1.match(username):
print(pattern1.groups)
print(pattern1.match(username))
print('username success')
else:
print('username error')
pattern2 = (re.match(r'^1[38][0-9]\d{8}$', phone_num)) or
(re.match(r'^1[45][0-35-9]\d{8}$', phone_num)) or (re.match(r'^17[6-8]\d{8}$', phone_num))
if pattern2:
print('phone_number success')
else:
print('phone_number error')
def main():
my_username = input('UserName:')
# 从头匹配
juct1 = re.match(r'\w{6,20}', my_username)
# 从中间匹配
juct2 = re.search(r'\w{6,20}', my_username)
# 完整匹配
juct3 = re.search(r'^\w{6,20}$', my_username)
if juct1:
print(re.match(r'\w{6,20}', my_username))
print(juct1.span())
print(juct1.group())
print('success')
else:
print(re.match(r'\w{6,20}', my_username))
print('error')
进程与线程
进程
进程是操作系统的分配内存的基本单位,进程之间的内存是相互隔离的
如果进程之间想要通信就要通过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('图片已接收')