• Python-Python高阶技巧:闭包、装饰器、设计模式、多线程、网络编程、正则表达式、递归


    版本说明

    当前版本号[20231018]。

    版本修改说明
    20231018初版

    目录

    Python高阶技巧

    闭包

    image-20230731205024239

    image-20230731205031917

    通过全局变量account_amount来记录余额

    尽管功能实现是ok的,但是仍有问题:

    • 代码在命名空间上(变量定义)不够干净、整洁
    • 全局变量有被修改的风险

    如何解决?

    • 将变量定义在函数内部是行不通的
    • 我们需要使用闭包

    在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包

    image-20230731205215934

    简单闭包

    image-20230731205250838

    修改外部函数变量的值

    image-20230731205744964

    实现以下atm取钱的闭包实现了

    image-20230731205822732

    闭包注意事项

    优点,使用闭包可以让我们得到:

    无需定义全局变量即可实现通过函数,持续的访问、修改某个值

    •闭包使用的变量的所用于在函数内,难以被错误的调用修改

    缺点:

    •由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存

    示例代码:

    def account_create(initial_amount=0):
        def atm(num, deposit=True):
            nonlocal initial_amount
            if deposit:
                initial_amount += num
                print(f"存款:+{num}, 账户余额:{initial_amount}")
            else:
                initial_amount -= num
                print(f"存款:-{num}, 账户余额:{initial_amount}")
    
        return atm
    
    atm = account_create()
    atm(300)
    atm(200)
    atm(100, False)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    装饰器

    装饰器其实也是一种闭包, 其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能

    image-20230731210813945

    希望给sleep函数,增加一个功能:

    •在调用sleep前输出:我要睡觉了

    •在调用sleep后输出:我起床了

    装饰器的一般写法(闭包写法)

    image-20230731210912523

    装饰器的语法糖写法

    image-20230731210949632

    示例代码:

    def outer(func):
        def inner():
            print("我要睡觉了")
            func()
            print("我起床了")
        return inner
    
    @outer
    def sleep():
        import random
        import time
        print("睡眠中……")
        time.sleep(random.randint(1, 5))
    
    sleep()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    设计模式

    设计模式是一种编程套路,可以极大的方便程序的开发。

    最常见、最经典的设计模式,就是我们所学习的面向对象了。

    除了面向对象外,在编程中也有很多既定的套路可以方便开发,我们称之为设计模式:

    • 单例、工厂模式
    • 建造者、责任链、状态、备忘录、解释器、访问者、观察者、中介、模板、代理模式
    • 等等模式

    单例模式

    image-20230731211135653

    创建类的实例后,就可以得到一个完整的、独立的类对象。

    通过print语句可以看出,它们的内存地址是不相同的,即t1和t2是完全独立的两个对象。

    image-20230731211149898

    某些场景下, 我们需要一个类无论获取多少次类对象,都仅仅提供一个具体的实例

    用以节省创建类对象的开销和内存开销,比如某些工具类,仅需要1个实例,即可在各处使用

    这就是单例模式所要实现的效果。

    单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。

    在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

    • 定义: 保证一个类只有一个实例,并提供一个访问它的全局访问点
    • 适用场景:当一个类只能有一个实例,而客户可以从一个众所周知的访问点访问它时。
    单例的实现模式

    image-20230731211352989

    工厂模式

    当需要大量创建一个类的实例的时候, 可以使用工厂模式。

    即,从原生的使用类的构造去创建对象的形式迁移到,基于工厂提供的方法去创建对象的形式。

    image-20230731211609598

    • 使用工厂类的get_person()方法去创建具体的类对象

    优点:

    • 大批量创建对象的时候有统一的入口,易于代码维护
    • 当发生修改,仅修改工厂类的创建方法即可
    • 符合现实世界的模式,即由工厂来制作产品(对象)

    示例代码:

    class Person:
        pass
    
    class Worker(Person):
        pass
    
    class Student(Person):
        pass
    
    class Teacher(Person):
        pass
    
    class PersonFactory:
        def get_person(self, p_type):
            if p_type == 'w':
                return Worker()
            elif p_type == 's':
                return Student()
            else:
                return Teacher()
    
    pf = PersonFactory()
    worker = pf.get_person('w')
    stu = pf.get_person('s')
    teacher = pf.get_person('t')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    多线程

    进程、线程和并行执行

    进程、线程

    现代操作系统比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任务”的操作系统。

    进程: 就是一个程序,运行在系统之上,那么便称之这个程序为一个运行进程,并分配进程ID方便系统管理。

    线程:线程是归属于进程的,一个进程可以开启多个线程,执行不同的工作,是进程的实际工作最小单位。

    进程就好比一家公司,是操作系统对程序进行运行管理的单位

    线程就好比公司的员工,进程可以有多个线程(员工),是进程实际的工作者

    操作系统中可以运行多个进程,即多任务运行

    一个进程内可以运行多个线程,即多线程运行

    注意点:

    进程之间是内存隔离的, 即不同的进程拥有各自的内存空间。 这就类似于不同的公司拥有不同的办公场所。

    线程之间是内存共享的,线程是属于进程的,一个进程内的多个线程之间是共享这个进程所拥有的内存空间的。

    这就好比,公司员工之间是共享公司的办公场所。

    image-20230731212151538

    并行执行

    并行执行的意思指的是同一时间做不同的工作

    进程之间就是并行执行的,操作系统可以同时运行好多程序,这些程序都是在并行执行。

    除了进程外,线程其实也是可以并行执行的。

    也就是比如一个Python程序,其实是完全可以做到:

    • 一个线程在输出:你好
    • 一个线程在输出:Hello

    像这样一个程序在同一时间做两件乃至多件不同的事情, 我们就称之为:多线程并行执行

    多线程编程

    threading模块

    绝大多数编程语言,都允许多线程编程,Pyhton也不例外。

    Python的多线程可以通过threading模块来实现。

    image-20230731212446548

    image-20230731212526327

    image-20230731212551780

    多线程练习案例

    import time
    import threading
    
    def sing(msg):
        while True:
            print(msg)
            time.sleep(1)
    
    def dance(msg):
        while True:
            print(msg)
            time.sleep(1)
    
    if __name__ == '__main__':
        #创建一个唱歌的线程
        sing_thread = threading.Thread(target=sing, args=("我要唱歌!",))
        #创建一个跳舞的线程
        dance_thread = threading.Thread(target=dance, kwargs={"msg": "我在跳舞!"})
    
    #使线程开始工作
    sing_thread.start()
    dance_thread.start()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    网络编程

    服务端开发

    Socket

    socket (简称 套接字) 是进程之间通信一个工具,好比现实生活中的插座,所有的家用电器要想工作都是基于插座进行,进程之间想要进行网络通信需要socket。

    Socket负责进程之间的网络数据传输,好比数据的搬运工。

    image-20230731212757604

    客户端和服务端

    2个进程之间通过Socket进行相互通讯,就必须有服务端和客户端

    Socket服务端:等待其它进程的连接、可接受发来的消息、可以回复消息

    Socket客户端:主动连接服务端、可以发送消息、可以接收回复

    image-20230731212905181

    Socket服务端编程

    主要分为如下几个步骤:

    1. 创建socket对象

    image-20230731213004113

    1. 绑定socket_server到指定IP和地址

    image-20230731213010971

    1. 服务端开始监听端口

    image-20230731213017624

    1. 接收客户端连接,获得连接对象

    image-20230731213023846

    1. 客户端连接后,通过recv方法,接收客户端发送的消息

    image-20230731213048654

    1. 通过conn(客户端当次连接对象),调用send方法可以回复消息

    image-20230731213057314

    1. conn(客户端当次连接对象)和socket_server对象调用close方法,关闭连接
    实现服务端并结合客户端进行测试

    image-20230731213211183

    下载网络调试助手作为客户端

    https://github.com/nicedayzhu/netAssist/releases

    image-20230731213230792

    示例代码:

    #演示Socket服务端开发
    
    import socket
    #创造Socket对象
    socket_server = socket.socket()
    #修改ip地址和端口
    socket_server.bind(("localhost", 8888))
    #监听端口
    socket_server.listen(1)
    #listen 方法内接受一个整数传参数, 表示接受的链接数量
    #等待客户端链接
    #result: tuple = socket_server.accept()
    #conn = result[0]    客户端和服务端的链接对象
    #address = result[1]  客户端的地址信息
    conn, address = socket_server.accept()
    #accept方法返回的是二元元组(链接对象,客户端地址信息)
    #可以通过 变量1, 变量2 = socket_server.accept()的形式,直接接受二元元组内的两个元素
    #accept()方法,是阻塞的方法,等待客户端的链接,如果没有链接,就卡在这一行不再向下执行
    
    print(f"我接收到了客户端的链接,客户端的信息是:{address}")
    
    while True:
        #接受客户端的信息,要使用客户端和服务端的本次链接对象,而非socket_server对象
        data: str = conn.recv(1024).decode("UTF-8")
        #recv接受的参数是缓冲区大小,一般1024即可
        #recv方法的返回值是一个字节数组也就是bytes对象,不是字符串,可通过decade方法通过UTF-8编码,将字节数组转换为字符串对象
        print(f"客户端发来的信息是:{data}")
        #发送回复消息
        msg = input("请输入你要和客户端回复的消息:")
        if msg == 'exit':
            break
        conn.send(msg.encode("UTF-8"))
    
    #关闭链接
    conn.close()
    socket_server.close()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    客户端开发

    Socket客户端编程

    主要分为如下几个步骤:

    1. 创建socket对象

    image-20230731213335132

    1. 连接到服务端

    image-20230731213341719

    1. 发送消息

    image-20230731213347304

    1. 接收返回消息

    image-20230731213408106

    1. 关闭链接

    image-20230731213419783

    服务端客户端相互通讯

    结合上一节学习的服务端代码,以及当前学习的客户端代码。

    两者均运行起来,进行相互通讯。

    image-20230731213505005

    正则表达式

    基础匹配

    正则表达式,又称规则表达式(Regular Expression),是使用单个字符串来描述、匹配某个句法规则的字符串,常被用来检索、替换那些符合某个模式(规则)的文本。

    简单来说,正则表达式就是使用:字符串定义规则,并通过规则去验证字符串是否匹配。

    比如,验证一个字符串是否是符合条件的电子邮箱地址,只需要配置好正则规则,即可匹配任意邮箱。

    比如通过正则规则: (1+(.[\w-]+)*@[\w-]+(.[\w-]+)+$) 即可匹配一个字符串是否是标准邮箱格式

    但如果不使用正则,使用if else来对字符串做判断就非常困难了。

    正则的三个基础方法

    Python正则表达式,使用re模块,并基于re模块中三个基础方法来做正则匹配。

    分别是:match、search、findall 三个基础方法

    • re.match(匹配规则, 被匹配字符串)

    从被匹配字符串开头进行匹配, 匹配成功返回匹配对象(包含匹配的信息),匹配不成功返回空。

    image-20230731213638041

    • search(匹配规则, 被匹配字符串)

    搜索整个字符串,找出匹配的。从前向后,找到第一个后,就停止,不会继续向后

    image-20230731213915705

    整个字符串都找不到,返回None

    image-20230731213924313

    • findall(匹配规则, 被匹配字符串)

    匹配整个字符串,找出全部匹配项

    image-20230731213951142

    找不到返回空list: []

    image-20230731213957525

    1. re模块的三个主要方法
    • re.match,从头开始匹配,匹配第一个命中项
    • re.search,全局匹配,匹配第一个命中项
    • re.findall,全局匹配,匹配全部命中项

    元字符匹配

    在刚刚我们只是进行了基础的字符串匹配,正则最强大的功能在于元字符匹配规则。

    单字符匹配

    image-20230731214130317

    示例:

    字符串 s = “itheima1 @@python2 !!666 ##itcast3”

    • 找出全部数字:
    re.findall(r‘\d’, s)
    
    • 1

    字符串的r标记,表示当前字符串是原始字符串,即内部的转义字符无效而是普通字符

    • 找出特殊字符:
    re.findall(r‘\W’, s)
    
    • 1
    • 找出全部英文字母:
    re.findall(r’[a-zA-Z]’, s)
    
    • 1

    []内可以写:[a-zA-Z0-9] 这三种范围组合或指定单个字符如[aceDFG135]

    数量匹配

    image-20230731214258248

    边界匹配

    image-20230731214320316

    分组匹配

    image-20230731214334658

    递归

    递归在编程中是一种非常重要的算法

    递归: 即方法(函数)自己调用自己的一种特殊编程写法

    如:

    image-20230731214406613

    函数调用自己,即称之为递归调用。

    递归找文件

    最典型的递归场景为找出一个文件夹中全部的文件。

    如图,在D:/test 文件夹内,有如下嵌套结构和所属的文件, 可以通过递归编程的形式完成

    image-20230731214440178

    image-202307312144481853. os模块的3个方法

    • os.listdir,列出指定目录下的内容
    • os.path.isdir,判断给定路径是否是文件夹,是返回True,否返回False

    1. \w- ↩︎

  • 相关阅读:
    Cookie的使用细节
    一些比较好的行为识别的项目链接
    11-15 周三 softmax 回归学习
    A brief Introduction to Continue Learning / Life long Learning
    西瓜书-2.5偏差与方差
    纯内网环境中k8s下onlyOffice启用https
    python入门基础
    【后端】Ubuntu开放mysql端口访问;如何开放服务器mysql给其他ip使用;在Ubuntu/Linux环境下开放3306端口
    基于Java的校园跑腿接单管理系统设计与实现(源码+lw+部署文档+讲解等)
    【javaEE】——文件和IO操作05
  • 原文地址:https://blog.csdn.net/weixin_65106708/article/details/133896890