多重继承、正则表达式、多线程、网络编程

多重继承

多重继承:子类继承多个父类的属性
父类中有重复的时候按先后顺序执行,执行排在前面的 如果一个类都多个父类,而多个父类又有公共的父类,那么在搜索属性和方法时搜索的依据是c3算法–类似广度优先搜索(先搜索统一层级的,专门处理菱形继承)
最好不要使用多重继承如果无法避免就把不是首要类父类写成抽象类,只是做一个约定,要求子类要写哪些东西。

 from abc import ABCMeta, abstractmethod


class Father(object):


    def __init__(self,name):
        self._name = name

    def drink(self):
        print(self._name + '正在喝二锅头')

    def ganmble(self):
        print(self._name + '正在赌博')

class Monk(object,metaclass = ABCMeta):
    def __init__(self,nickname):
        self._nickname = nickname

    @abstractmethod
    def eat_vegetable(self):
        pass

    @abstractmethod
    def drink(self):
        print(self._name + '正在喝水')


class Musican(object,metaclass = ABCMeta):
    def __init__(self,artname):
        self._artname = artname

    @abstractmethod
    def playpinao(self):
        pass


class Son(Father,Monk,Musican):
    def __init__(self,name,nickname,artname):
        self._name = name
        self._nickname = nickname
        self._artname = artname

    def eat_vegetable(self):
        print(self._name + '正在吃斋')

    def playpinao(self):
        print(self._name + '正在弹钢琴')

    def drink(self):
        print(self._name + '正在喝饮料')
        #重写

def main():
    son = Son('王大锤', '锤锤', '王小锤')
    son.drink()
    son.eat_vegetable()
    son.playpinao()
    son.ganmble()

if __name__ == '__main__':
    main()

运行结果:

王大锤正在喝饮料   #继承重写后的
锤锤正在吃斋
王小锤正在弹钢琴
王大锤正在赌博

正则表达式

正则表达式是一种用来匹配字符串的强有力的武器。它的设计思想是用一种描述性的语言来给字符串定义一个规则,凡是符合规则的字符串,我们就认为它“匹配”了,否则,该字符串就是不合法的。
Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。re 模块使 Python 语言拥有全部的正则表达式功能。
compile 函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换,re 模块也提供了与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数。

常见正则表达式参数

表1.常用的元字符

代码 说明
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束
^ 匹配字符串的开始
$ 匹配字符串的结束

如果你想查找元字符本身的话,比如你查找.,或者*,就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用\来取消这些字符的特殊意义。因此,你应该使用.和*。当然,要查找\本身,你也得用\.

例如:’deerchao.net’匹配’deerchao.net’,
‘C:\Windows’匹配’C:\Windows’,或者用r’C:\Windows’r表示不转义,原封不动输出

表2.常用的限定符

代码 说明
* 重复零次或更多次
+ 重复一次或更多次
重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n次到m次

re.match函数

re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。
标准格式是
re.match(pattern, string, flags=0)

参数 说明
pattern 匹配的正则表达式
string 匹配的字符串
flags 一个标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

我们可以使用group(num)groups() 匹配对象函数来获取匹配表达式。

参数 说明
group(num=0) 匹配的整个表达式的字符串
group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。
import re

m = re.match(r'^(\d{3})-(\d{3,8})$', '028-7160370')
print(m.group(0))
print(m.group(1))
print(m.group(2))

运行结果如下:

028-7160370
028
7160370

re.search方法

re.match不同的是re.search是在字符串中查找内容,它将扫描整个字符串并返回第一个成功的匹配。
标准格式是
re.search(pattern, string, flags=0)
其参数的含义与re.match中的一样,在re.search中也可以使用group方法来搜索字符串。

import re

line = "Python is beautiful and gental"

searchObj = re.search(r'(.*) is (.*?) .*', line, re.M | re.I)

if searchObj:
    print(searchObj.group())
    print(searchObj.group(1))
    print(searchObj.group(2))
else:
    print("Nothing found!!")

运行结果如下:

Python is beautiful and gental
Python
beautiful

上面的代码中用到了|,这是一个表示分支条件的符号,这里就不多做介绍了。

re.compile 函数

re.compile 函数用于编译正则表达式,生成一个正则表达式(Pattern )对象,供 re.match()re. search() 这两个函数使用。
语法格式为:

re.compile(pattern[, flags])

在实际编程中,re.compile 函数是一种非常实用的工具。

import re


def main():

    QQ_number = input('请输入QQ号:')
    user_name = input('请输入用户名')
    pattern1 = re.compile(r'^[1-9]\d{4,11}$')
    pattern2 = re.compile(r'^[0-9a-zA-Z_]{4,11}')
    m1 = pattern1.match(QQ_number)
    m2 = pattern2.match(user_name)
    if m1 and m2:
        print('有效的用户名和密码')
    else:
        print('你是不是傻')


if __name__ == '__main__':

    main()

运行结果为:

请输入QQ号:550276104
请输入用户名dsfsfsdf
有效的用户名和密码

请输入QQ号:3y4783buf
请输入用户名huwig8234
你是不是傻

替换

正则表达式里的替换指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开。Python 的 re 模块提供了re.sub用于替换字符串中的匹配项。
标准表达式:

re.sub(pattern,*, string, count=0, flags=0)
*:提供替换的字符
count:模式匹配后替换的最大次数,默认 0 表示替换所有的匹配

下面我们举一个有趣的例子,用re.sub可以自动屏蔽聊天中的不文明字符。

import re

def main():

    sentence = '麻花是个笨蛋,是一个大傻子,也是一个大坑逼,fuck'
    pure = re.sub('[笨蛋]|大傻子|坑逼|FUCK', '*', sentence, flags=re.I)#替换字符中的而不良内容,
    # re.I表示忽略大小写
    print(pure)


if __name__ == '__main__':
    main()

运行结果如下:

麻花是个**,是一个*,也是一个大*,*

findall

在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
注意: match 和 search 是匹配一次 findall 匹配所有。
语法格式为:

findall(pattern, string, flags)
import re
from re import findall


def main():
    pattern = re.compile(r'1[345789]\d{9}')
    sentence = '我的手机号是13512345678,不是13300998765,' \
               '傻吊的手机号是18010675853,而不是110,' \
               '重要的事情说1150403030300次'

    mylist = pattern.findall(sentence)#一次吧所有的都取出来
    print(mylist)

if __name__ == '__main__':
    main()

执行结果如下:

['13512345678', '13300998765', '18010675853', '15040303030']

我们注意到列表中返回的值都是字符串格式,那么我们可以通过map(int,mylist)来将mylist中的元素改写成int格式。

finditer

findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。注意finditer是将结果一个个返回的。以上面findall的代码为例:

import re
from re import findall


def main():
    pattern = re.compile(r'1[345789]\d{9}')
    sentence = '我的手机号是13512345678,不是13300998765,' \
               '傻吊的手机号是18010675853,而不是110,' \
               '重要的事情说1150403030300次'

    mylist = pattern.findall(sentence)#一次把所有的都取出来
    print(mylist)
        for temp in pattern.finditer(sentence):#iter-iterator迭代器,一个一个取
        print(temp.group())
        print(temp.span())

if __name__ == '__main__':
    main()

执行结果如下:

['13512345678', '13300998765', '18010675853', '15040303030']
13512345678
(6, 17)
13300998765
(20, 31)
18010675853
(39, 50)
15040303030
(65, 76)

我么可以看到finditer的返回结果类似于循环的返回结果,是一个个值返回的。

re.split

split 方法按照能够匹配的子串将字符串分割后返回列表

import re


    sentence = 'You gou you way,i will go mine!'
    mylist = re.split(r'[ ,!]', sentence)#拆分空格感叹号和逗号之间的字符
    print(mylist)

执行结果如下:

['You', 'gou', 'you', 'way', 'i', 'will', 'go', 'mine', '']

贪婪与懒惰匹配

当正则表达式中包含能接受重复的量词(指定数量的代码)时,通常的行为是匹配尽可能多的字符。考虑这个表达式:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的量词都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧:
a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aabab

代码 说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复1次或更多次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

多线程和多进程

多线程

线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。
线程越多,调用cpu的几率越大,占用CPU越多。如果一个任务占用时间很长,那么可以调用多线程模式来提升执行效率,缩短程序执行时间,改善用户体验。
另外,线程可以被抢占(中断),在其他线程正在运行时,线程可以暂时搁置(也称为睡眠) – 这就是线程的退让。
创建线程的两种方式:
1.直接创建Thread对象并通过target参数指定线程启动后要执行的任务
2.继承Thread自定义线程,通过重写run方法指定线程启动后执行的任务
在编写代码时我们通常使用from threading import Thread 来引入多线程

#在屏幕上打印pingpong
from threading import Thread  # 引入多线程

count = 0


def output(string):
    global count
    while count < 10:
        print(string, end='', flush=True)
        count += 1


def main():
    t1 = Thread(target=output, args=('ping',), daemon=True).start()
    # daemen将这个线程设置为守护线程,主线程结束后跟着而结束,没有守护线程进程不会停止
    t2 = Thread(target=output, args=('pong',), daemon=True).start()


class PrintThread(Thread):
    def __init__(self, string, count):
        super(PrintThread, self).__init__()
        self._string = string
        self._count = count

    def run(self):
        for _ in range(self._count):
            print(self._string, end='', flush=True)
       #flush = True 简单来讲就是取消缓存,将内容直接输出到屏幕上


def main():
    PrintThread('ping', 10).start() #start()表示启动线程
    PrintThread('pong', 10).start()


if __name__ == '__main__':
    main()

利用多线程做一个银行卡

import time
from threading import Thread, Lock


class Account(object):
    def __init__(self):
        self._balance = 0
        self._lock = Lock()

    @property
    def balance(self):
        return self._balance

    def deposit(self, money):
    #当多个线程同时访问一个资源的时候就有可能由于竞争资源而导致资源的状态错误
    #被多个线程访问的资源我们通常称为临界资源,对临界资源的访问需要加上保护
        if money > 0:
            self._lock.acquire()
            #访问之前对资源加锁,只有自己能用,别的线程排队等待,会使时间变长
            try:
                new_balance = self._balance + money
                time.sleep(0.001)
                self._balance = new_balance
            finally:#保证能解锁,就算上一个线程出错
                self._lock.release()


class AddmoneyThread(Thread):
    def __init__(self, account):
        super(AddmoneyThread, self).__init__()
        self._account = account

    def run(self):
        self._account.deposit(1)


def main():
    account = Account()
    tlist = []
    for _ in range(100):
        t = AddmoneyThread(account)
        tlist.append(t)
        t.start()
    for t in tlist:
        t.join()
    print('账户余额:%d元' % account.balance)


if __name__ == '__main__':
    main()

多进程

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
需要注意的是进程之间的内存是相互隔离的,要通信要开IPC机制。
如果多个任务之间没有任何的关联(独立子任务)而且希望利用cpu的多核特性,那么我们推荐使用多进程。
通常我们使用from multiprocessing import 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('Child process will start.')
    p.start()
    p.join()
    print('Child process end.')

执行结果如下:

Parent process 4644.
Child process will start.
Run child process test (4492)...
Child process end.

网络编程

在了解网络编程之前我们先简单了解什么是计算机网络。
计算机网络,是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和 信息传递的计算机系统,可以自行查看百度上的介绍
TCP/UDP

  1. TCP协议则是建立在IP协议之上的。TCP协议负责在两台计算机之间建立可靠连接,保证数据包按顺序到达。TCP协议会通过握手建立连接,然后,对每个IP包编号,确保对方按顺序收到,如果包丢掉了,就自动重发。
    许多常用的更高级的协议都是建立在TCP协议基础上的,比如用于浏览器的HTTP协议、发送邮件的SMTP协议等。
    一个TCP报文除了包含要传输的数据外,还包含源IP地址和目标IP地址,源端口和目标端口。
  2. 除了TCP之外我们介绍另一种协议—UDP,相较于TCP来说UDP传输速度更快,但是它的传输是不稳定的,因为它没有TCP的握手机制。但是现数字信号传输错误的几率已经很小了,所以UDP也是很常用的。

Python 提供了两个级别访问的网络服务。:

  1. 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets
    API,可以访问底层操作系统Socket接口的全部方法。
  2. 高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。
    Python 中,我们用 socket()函数来创建套接字,继而绑定服务器和端口。
    这里写图片描述
    现在我们尝试做一个本机的服务器;
from socket import socket, AF_INET, SOCK_STREAM  # TCP协议
import datetime


def main():
    # 创建一个基于TCP协议(默认)的服务器套接字对象
    # 因为我们做的是应用级的服务,所以可以利用现有的传输服务来实现数据传输
    server = socket()
    # 绑定网卡端口(用来区分不同服务的IP地址扩展)和IP地址(网络上主机的身份标识)
    server.bind(('10.7.189.101', 1234))
    # 开始监听客户端的连接
    server.listen(512)  # 队列大小
    print('服务器已经启动,正在监听...')
    client, addr = server.accept()# 返回一个元组
    print(addr, '连接成功')
    while True:
        # 通过accept方法接受客户端的连接
        #  accept是一个阻塞式的方法,如果没有客户端连上来,那么accept就会让代码阻塞,
        # 直到有客户端连接上来
        # accept方法返回一个元组,元组中的第一个值代表客户端的对象
        # 第二个值又是一个元组,其中有客户端的IP地址和客户端的端口
        word = input('请输入:')#给对方发消息
        client.send(word.encode('utf-8'))#编码指定为UFT-8
        #指定最多有多少能排在序列中
        data = client.recv(1024)#
        print(data.decode('utf-8'))


if __name__ == '__main__':
    main()

那么当别人连接上我的服务器的时候就会显示:

服务器已经启动,正在监听...
('10.7.189.101', 58013) 连接成功
请输入:

这样就能实现通过服务器和客户端交流,那么我们再做一个简单的客户端:

from socket import socket


def main():
    client = socket()
    #这里连接对方服务器的IP地址和端口
    client.connect(('10.7.189.101', 1234))
    while True:
        data = client.recv(1024)
        print(data.decode('utf-8'))
        word = input('请输入:')
        client.send(word.encode('utf-8'))


if __name__ == '__main__':
    main()

自动发邮件

SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。
python的smtplib提供了一种很方便的途径发送电子邮件。它对smtp协议进行了简单的封装。

#发送文本文件
import smtplib
from email.mime.text import MIMEText
from email.header import Header

sender = '[email protected]'
receivers = ['[email protected]']  # 接收邮件,可设置为你的QQ邮箱或者其他邮箱

# 三个参数:第一个为文本内容,第二个 plain 设置文本格式,第三个 utf-8 设置编码
message = MIMEText('Python 邮件发送测试...', 'plain', 'utf-8')
message['From'] = Header("菜鸟教程", 'utf-8')
message['To'] =  Header("测试", 'utf-8')

subject = 'Python SMTP 邮件测试'
message['Subject'] = Header(subject, 'utf-8')


try:
    smtpObj = smtplib.SMTP('localhost')
    smtpObj.sendmail(sender, receivers, message.as_string())
    print ("邮件发送成功")
except smtplib.SMTPException:
    print ("Error: 无法发送邮件")
#发送带有附件的邮件
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from smtplib import SMTP


def main():
    sender = SMTP('smtp.163.com')
    sender.login('[email protected]', 'hp1994314')
    message = MIMEMultipart()
    text_msg = MIMEText('本月工作总结')
    # MIME - Multipurpose Internet Mail Extension
    # 邮件主题
    message['subject'] = '学习总结'
    message.attach(text_msg)
    att1 = MIMEText(open('Microsoft Word.docx', 'rb').read(), 'base64', 'utf-8')
    # 文件的MIME类型,可以在http://www.w3school.com.cn/media/media_mimeref.asp查看
    att1["Content-Type"] = 'application/octet-stream'
    att1["Content-Disposition"] = 'attachment; filename="foo.docx"'#附件名字
    message.attach(att1)
    sender.sendmail('[email protected]',
                    ['[email protected]',
                                            '[email protected]'], message.as_string())
    print('邮件发送完成')


if __name__ == '__main__':
    main()

练习

最后附上两道练习题

  1. 用多线程和pygame做一个赛车小游戏
from random import randint
from threading import Thread
import pygame
import time


class Color(object):
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    GRAY = (242, 242, 242)

    @staticmethod
    def random_color():
        r = randint(0, 255)
        g = randint(0, 255)
        b = randint(0, 255)
        return r, g, b


class Car(object):

    def __init__(self,x, y, color):
        self._x = x
        self._y = y
        self._color = color

    def move(self):
        if self._x + 80 < 950:
            self._x += randint(1, 10)

    def draw(self, screen):
        pygame.draw.rect(screen, self._color,
                         (self._x, self._y, 80, 40), 0)

def main():
    class BackGroundTask(Thread):
        def run(self):
            while True:
                time.sleep(0.05)
                screen.fill(Color.GRAY)
                pygame.draw.line(screen, Color.BLACK, (130, 0), (130, 600), 4)
                pygame.draw.line(screen, Color.BLACK, (950, 0), (950, 600), 4)
                for car in cars:
                    car.draw(screen)
                    car.move()
                pygame.display.flip()
    cars = []
    for index in range(5):
        temp = Car(50, 50 + 120 * index, Color.random_color())
        cars.append(temp)
    pygame.init()
    screen = pygame.display.set_mode((1000, 600))
    BackGroundTask(daemon=True).start()
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

    pygame.quit()


if __name__ == '__main__':
    main()
  1. 利用多线程做一个聊天室,实现客户端之间可以聊天功能
#服务器端
from socket import socket
from threading import Thread


def main():

    class ClientHandler(Thread):

        def __init__(self, client):
            super().__init__()
            self._client = client

        def run(self):
            try:
                while True:
                    try:
                        data = self._client.recv(1024)
                        #如果有人说'byebue'就自动离开聊天室
                        if data.decode('utf-8') == 'byebye':
                            clients.remove(self._client)
                            self._client.close()
                            break
                        else:
                            for client in clients:
                                client.send(data)
                    except Exception as e:
                        print(e)
                        clients.remove(self._client)
                        break
            except Exception as e:
                print(e)

    server = socket()
    server.bind(('10.7.189.101', 12345))
    server.listen(512)
    clients = []
    while True:
        curr_client, addr = server.accept()
        print(addr[0], '连接到服务器.')
        clients.append(curr_client)
        ClientHandler(curr_client).start()


if __name__ == '__main__':
    main()
#客户端
from socket import socket
from threading import Thread


def main():

    class RefreshScreenThread(Thread):

        def __init__(self, client):
            super().__init__()
            self._client = client

        def run(self):
            while running:
                data = self._client.recv(1024)
                print(data.decode('utf-8'))

    nickname = input('请输入你的昵称: ')
    myclient = socket()
    myclient.connect(('10.7.189.71', 4096))
    running = True
    RefreshScreenThread(myclient).start()
    while running:
        content = input('请发言: ')
        if content == 'byebye':
            myclient.send(content.encode('utf-8'))
            running = False
        else:
            msg = nickname + ': ' + content
            myclient.send(msg.encode('utf-8'))


if __name__ == '__main__':
    main()

猜你喜欢

转载自blog.csdn.net/qq_41768400/article/details/79674787
今日推荐