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

news2025/7/16 3:28:10

版本说明

当前版本号[20231018]。

版本修改说明
20231018初版

目录

文章目录

  • 版本说明
  • 目录
  • Python高阶技巧
    • 闭包
      • 简单闭包
      • 修改外部函数变量的值
      • 实现以下atm取钱的闭包实现了
      • 闭包注意事项
    • 装饰器
      • 装饰器的一般写法(闭包写法)
      • 装饰器的语法糖写法
    • 设计模式
      • 单例模式
        • 单例的实现模式
      • 工厂模式
    • 多线程
      • 进程、线程和并行执行
        • 进程、线程
        • 并行执行
      • 多线程编程
        • threading模块
      • 多线程练习案例
    • 网络编程
      • 服务端开发
        • Socket
        • 客户端和服务端
        • Socket服务端编程
        • 实现服务端并结合客户端进行测试
      • 客户端开发
        • Socket客户端编程
        • 服务端客户端相互通讯
    • 正则表达式
      • 基础匹配
        • 正则的三个基础方法
      • 元字符匹配
        • 单字符匹配
        • 数量匹配
        • 边界匹配
        • 分组匹配
    • 递归
      • 递归找文件

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)

装饰器

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

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()

设计模式

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

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

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

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

单例模式

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')

多线程

进程、线程和并行执行

进程、线程

现代操作系统比如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()

网络编程

服务端开发

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()

客户端开发

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)

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

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

[]内可以写:[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- ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1104063.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序中如何使用fontawesome6的免费图标

一、官网下载fontawesome6 Download Font Awesome Free or Pro | Font Awesome 二、使用transfer编码成Base64 transfer打开官网:Online font-face generator — Transfonter 首先先把刚刚下载的fontawesome6解压,将文件夹中的字体上传(点…

发电机组负载测试的必要性

发电机组负载测试是确保发电机组能够在实际运行中稳定工作的重要步骤,负载测试可以模拟发电机组在不同负载条件下的工作情况,评估其性能和稳定性。负载测试可以验证发电机组在不同负载条件下的性能表现,通过模拟实际使用情况评估发电机组的输…

【Flutter】第一篇基础:站在一名web前端开发者的角度看代框架

Flutter Flutter 是一个跨平台的 UI 工具集,它的设计初衷,就是允许在各种操作系统上复用同样的代码,例如 iOS 和 Android,同时让应用程序可以直接与底层平台服务进行交互。如此设计是为了让开发者能够在不同的平台上,…

怎么把m4v转换为mp4?

怎么把m4v转换为mp4?M4V是一种由苹果公司开发的视频文件格式,该格式可以在苹果公司的iTunes和QuickTime软件中播放。M4V格式本质上与MP4格式相似,但M4V通常包括了用于数字版权管理(DRM)的保护措施,以控制该…

【笔记-OrCAD】WARNING(ORCAP-36038)解决办法

问题描述: OrCAD16.6绘制好原理图后,点击“*.dsn”文件可以生成网表,在存放原理图的文件内找到allegro文件夹,用记事本打开netlist.log文件,可以看到具体的警告原因,例如: WARNING(ORCAP-36038)…

优雅而高效的JavaScript—— Class 和模块化

😊博主:小猫娃来啦 😊文章核心:优雅而高效的JavaScript—— Class 和模块化 文章目录 引言Class 的概念和用法Class 的定义Class 的继承Class 的静态方法和属性 模块化的概念和用法模块的导出和导入模块的默认导出和命名导出模块的…

SpringCloud: sentinel链路限流

一、配置文件要增加 spring.cloud.sentinel.webContextUnify: false二、在要限流的业务方法上使用SentinelResource注解 package cn.edu.tju.service;import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockExcept…

CVPR、ICCV、ECCV论文获取

CVPR每年召开,ICCV两年一次 链接地址 ECCV两年一开 链接地址

10. 机器学习-评测指标

Hi,你好。我是茶桁。 之前的课程中,我们学习了两个最重要的回归方法,一个线性回归,一个逻辑回归。也讲解了为什么学习机器学习要从逻辑回归和线性回归讲起。因为我们在解决问题的时候,有限选择简单的假设,越复杂的模型…

十七、文件(1)

本章概要 文件和目录路径 选取路径部分片段路径分析Paths 的增减修改 目录 在丑陋的 Java I/O 编程方式诞生多年以后,Java终于简化了文件读写的基本操作。 打开并读取文件对于大多数编程语言来说是非常常用的,由于 I/O 糟糕的设计以至于很少有人能够在不…

第七章 排序

第七章 排序 概述插入排序交换排序冒泡排序快速排序 选择排序直接选择排序堆排序 归并排序有序序列合并二路归并排序 小试牛刀 概述 排序就是将一组对象按照规定的次序(升序或降序等)重新排列的过程,往往为检索服务相同键值的两个记录在排序…

索引背后的数据结构——B+树

为什么要使用B树? 可以进行数据查询的数据结构有二叉搜索树、哈希表等。对于前者来说,树的高度越高,进行查询比较的时候访问磁盘的次数就越多。而后者只有在数据等于key值的时候才能进行查询,不能进行模糊匹配。所以出现了B树来解…

SQL数据库管理工具RazorSQL mac中文版特点与功能

RazorSQL mac是一款功能强大的SQL数据库管理工具,它支持多种数据库,包括MySQL、Oracle、Microsoft SQL Server、SQLite、PostgreSQL等。 RazorSQL mac 软件特点和功能 多种数据库支持:RazorSQL支持多种数据库,用户可以通过一个工…

故障预测与健康管理(PHM)在工业领域的发展前景

故障预测与健康管理(PHM)作为一种关键技术,已经在工业领域引起了广泛的关注和应用。PHM利用传感器、数据科学和智能算法等技术手段,通过实时监测和分析设备和系统的状态,提前发现潜在故障,并采取适当的维修…

制作linux系统内部yum源仓库

需求说明 制作内网linux系统yum源仓库,比较简单的方式就是添加系统镜像,此种yum配置方式可参考文章 https://blog.csdn.net/d1240673769/article/details/108477661 如果无法提供系统镜像,那该如何创建内网的yum源仓库呢?本文提…

互联网Java工程师面试题·Java 总结篇·第六弹

目录 56、TreeMap 和 TreeSet 在排序时如何比较元素?Collections 工具类中的 sort()方法如何比较元素? 57、Thread 类的 sleep()方法和对象的 wait()方法都可以让线程暂停执行,它们有什么区别? 58、线程的 sleep()方法和 yield()方法有什…

在nodejs中实现双重身份验证机制

在nodejs中实现双重身份验证机制 双重身份验证(Two-factor authentication)是一种安全机制,它要求用户提供两种不同的身份验证因素来访问他们的帐户:密码和发送到他们的移动设备的验证码。在本文中,我们将一步步通过使用speakeasy在nodejs中实…

deforum + kandinsky = 视频工作流

像搭积木一样玩AI,随着模型种类的不断丰富,不同的组合会带来什么惊喜?今天和大家分享最近看到的一个视频工作流(工具箱)。 首先,我们先对deforum和kandinsky做一些基本的介绍: deforum-art/defo…

【论文解读】单目3D目标检测 CUPNet(ICCV 2021)

本文分享单目3D目标检测,CUPNet 模型的论文解读,了解它的设计思路,论文核心观点,模型结构,以及效果和性能。 目录 一、CUPNet简介 二、论文核心观点 三、模型框架 四、损失函数 五、核心观点——3D高度估计误差 引…

Python之并发编程(进程)

文章目录 一、操作系统的发展史二、进程基础(操作系统中的概念)1.什么是进程2.进程的调度算法3.进程的并行与并发4.进程的三状态5.同步异步6.阻塞与非阻塞7.同步异步与阻塞非阻塞综合使用 三、如何创建进程Process的几个方法如何开启多进程进程间的数据默认隔离基于TCP协议的高…