当前位置: 首页 > article >正文

python学习之路 - python进阶【闭包、装饰器、设计模式、多线程、socket、正则表达式】

目录

  • 一、python进阶
    • 1、闭包
        • a、概念
        • b、nonlocal关键字
        • c、优缺点
    • 2、装饰器
    • 3、设计模式
        • a、单例模式
        • b、工厂模式
    • 4、多线程
        • a、进程、线程
        • b、并行执行
        • c、多线程编程
    • 5、网络编程
        • a、概念
        • b、服务端开发
        • c、客户端开发
    • 6、正则表达式
        • a、概念
        • b、基础方法
        • c、元字符匹配
    • 7、递归

一、python进阶

1、闭包

a、概念

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

def outer(msg1):
    print(msg1)
    def inner(msg2):
        print(msg1 + "--" + msg2)
    return inner

out = outer("这是msg1")
out("这是msg2")
out("这是第二个msg2")

结果为:
这是msg1
这是msg1--这是msg2
这是msg1--这是第二个msg2
  • 如上述的案例,outer函数中的msg1只能作用域outer内部,外部无法获取到此变量
  • 外部函数的参数只能设置一次
  • 内部函数的变量可以设置多次,多次设置inner函数参数时,会多次执行inner函数内容
  • 内部函数只能使用外部函数的参数,此种写法无法更改参数内容
b、nonlocal关键字
  • 正常写法下,内部函数只能使用外部函数的参数,无法修改外部函数的参数,如果想要修改,则使用此关键字
  • 在内部函数中将外部函数的参数用nonlocal关键字修饰,则能更改参数的值
  • 多次调用内部函数时,多次的值会累加
def outer(msg1):
    def inner(msg2):
        nonlocal msg1
        msg1 += msg2
        print(f"{msg1}---{msg2}")
    return inner

out = outer(10)
out(10)
out(10)

结果为:
20---10
30---10
c、优缺点
  • 优点:
    • 无需定义全局变量即可实现通过函数,持续的访问修改某个值
    • 闭包使用的变量在函数内,难以被错误的调用修改
  • 缺点:
    • 由于内部函数持续引用外部函数的值,会导致部分内存空间不被释放,一直占用内存

2、装饰器

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

案例:定义方法睡眠5秒,睡前睡后分别输出内容

普通写法

from time import sleep
def func():
    print("开始")
    sleep(5)
    print("结束")
func()
装饰器简单写法

def outer(func):
    def inner():
        print("开始")
        func()
        print("结束")
    return inner

def sleep1():
	from time import sleep
    sleep(5)

out = outer(sleep1)
out()
装饰器高级写法

def outer(func):
    def inner():
        print("开始")
        func()
        print("结束")
    return inner

@outer
def sleep1():
    from time import sleep
    sleep(5)

sleep1()

3、设计模式

设计模式是一种编程套路,可以极大方便程序的开发
最常见的设计模式就是面向对象

a、单例模式

在普通情况下,定义了一个类,多次实例化时,所得到的地址是不一样的

class Test:
    pass
test1 = Test()
test2 = Test()
print(test1 == test2)

结果为:
False

如果想要多次实例化地址一样,就要用到单例模式
保证一个类只有一个实例,并且提供一个可以访问的全局访问点

第一步:test_tool.py文件中定义内容

class Test:
    pass

test_one = Test()
#第二步:test.py文件中定义内容

from test_tool import test_one
test1 = test_one
test2 = test_one
print(test1 == test2)

结果为:
True
b、工厂模式

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

当有多个类时,每次都要自己分别定义类才会获得对应对象

class Person:
    pass
class Student:
    pass

person = Person()
student = Student()

使用工厂模式,创建统一的创建对象的入口,便于代码维护
当发生修改时,只需要修改工厂类的方法即可

class Person:
    pass
class Student:
    pass

class Factory:
    def get_person(self,type):
        if type == "s":
            return Student()
        else:
            return Person()

person = Factory().get_person("p")
student = Factory().get_person("s")

4、多线程

a、进程、线程
  • 进程:就是一个程序,运行在系统之上,则称这个程序为一个运行进程,并分配进程ID方便系统管理
  • 线程:线程是归属于进程的,一个进程可以开启多个线程,执行不同的工作,是进程的实际工作量的最小单位
  • 注意:进程之间的内存是隔离的,则不同的进程拥有各自的内存空间。线程之间是内存共享的,线程属于进程,一个进程内的多个线程是共享这个进程所拥有的内存空间的
b、并行执行
  • 并行执行指同一时间做不同的工作
  • 进程之间就是并行执行的,操作系统可以同时运行很多程序,这些程序都是并行执行的
  • 线程也是可以并行执行的,同一个程序在同一时间做两件以上的事情,就称为多线程并行执行
c、多线程编程

python的多线程可以通过threading模块实现

基础语法

import threading
thread = threading.Thread(group=, target=, name=, args=, kwargs=, daemon=)
thread.start()

group:暂时无用,未来功能的预留函数
target:执行的目标任务名,就是方法名
args:以元组的方式给执行任务传参
kwargs:以字典的方式给执行任务传参
name:线程名,一般不用设置

案例:创建两个方法并使用多线程同时执行

def one(msg):
    while True:
        print(msg)
def two(msg):
    while True:
        print(msg)

import threading
#元组形式传参,只有一个元素时需要带上逗号才是元组
thread = threading.Thread(target=one, args=("one",))
#字典形式传参
thread2 = threading.Thread(target=two, kwargs={"msg": "two"})
thread.start()
thread2.start()

结果为:
one
twoone
twoone
two
two
......

5、网络编程

a、概念

socket:是进程之间通信的一个工具,负责进程之间的网络数据传输,好比数据的搬运工
2个进程之间通过socket进行相互通讯,就需要有服务端和客户端
socket服务端:等待其他进程的链接,接收客户端发来的信息并回复消息socket客户端:主动连接服务端,发送消息给服务端,并接收返回的信息

b、服务端开发
# 第一步:创建socket对象
socket_server = socket.socket()
# 第二步:为服务端绑定ip地址和端口
socket_server.bind(("127.0.0.1", 8888))
# 第三步:监听端口。
    # 参数为允许连接的数量
socket_server.listen(1)
# 第四步:等待客户端连接。
    # 会返回一个二元元组,第一个元素为客户端和服务端的连接对象,第二个元素为客户端的地址信息
    # accept方法是阻塞方法,如果没有客户端链接会一直停留在这一行
socket_client, addr = socket_server.accept()
# 第五步:接收客户端发送的数据。
    # 参数为接收数据的最大字节数
    # 返回的时一个字节数组,不是字符串,可以通过decode方法转换为字符串
data = socket_client.recv(1024).decode("utf-8")
print(f"客户端接收的消息为:{data}")
# 第六步:向客户端发送数据。
socket_client.send("hello".encode("utf-8"))
# 第七步:关闭连接
socket_client.close()   #此关闭的是客户端的连接
socket_server.close()   #此关闭的是服务端
c、客户端开发
import socket
# 第一步:创建socket对象
socket_client = socket.socket()
# 第二步:连接到服务端
socket_client.connect(("127.0.0.1", 8888))
# 第三步:发送消息
    # 发送的内容默认是通过字节传输的,通过encode方法将字符串转换为字节
socket_client.send("hello".encode("utf-8"))
# 第四步:接收返回的消息
    # 参数为接收数据的最大字节数,此方法为阻塞的
    # 返回的时一个字节数组,不是字符串,可以通过decode方法转换为字符串
recv_data = socket_client.recv(1024).decode("utf-8")
print(f"服务端回复的消息为:{recv_data}")
# 第五步:关闭连接
socket_client.close()   #此关闭的是客户端的连接

6、正则表达式

a、概念
  • 正则表达式:也称为规则表达式,是使用单个字符串来描述、匹配某个句法规则的字符串,常被用来检索、替换那些符合某个模式的文本
  • 简单来说,正则表达式就是使用字符串来定义规则,并通过规则来验证字符串是否匹配
b、基础方法
  • re.match(匹配规则,被匹配字符串)
    • 从被匹配字符串的开头进行匹配,如果匹配成功则返回匹配对象,如果匹配不成功则返回空
    • 注意是从开头匹配,如果中间有对应字符串是不算的

案例:找出字符串中是否包含python开头的字符串

#情况一
import re
s = "python test python test"
result = re.match("python",s)
print(result)
print(result.span())		#这里是打印匹配字符串开头和结束的下标
print(result.group())		#这里是打印匹配的字符串内容

结果为:
<re.Match object; span=(0, 6), match='python'>
(0, 6)
python
#情况二
import re
s = "1python test python test"
result = re.match("python",s)	#因为s是以1开头,所以无法匹配python字符串
print(result)

结果为:
None
  • re.search(匹配规则,被匹配字符串)
    • 搜索整个字符串,找出匹配的。从前向后,找到第一个后就会停止
    • 如果匹配成功,会返回匹配的位置信息。如果未匹配成功,会返回None

案例:找出字符串中是否包含python开头的字符串

#情况一
import re
s = "111python test python test"
result = re.search("python",s)
print(result)
print(result.span())	#这里返回的是匹配字符串的开头和结尾下标位置
print(result.group())	#这里返回的是匹配的字符串

结果为:
<re.Match object; span=(3, 9), match='python'>
(3, 9)
python
#情况二
import re
s = "111 test 2222 test"
result = re.search("python",s)
print(result)

结果为:
None
  • re.findall(匹配规则,被匹配字符串)
    • 会匹配整个字符串,找出全部的匹配项
    • 如匹配成功会返回全部的匹配字符串内容。如果匹配不成功,会返回空List
#情况一
import re
s = "111python test python test"
result = re.findall("python",s)
print(result)

结果为:
['python', 'python']
#情况二
import re
s = "111 test 2222 test"
result = re.findall("python",s)
print(result)

结果为:
[]
c、元字符匹配

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

  • 单字符匹配
字符功能
.匹配任意1个字符(除了\n),\. 就是匹配点本身
[ ]匹配 [ ] 中列举的字符
\d匹配数字,即 0-9
\D匹配非数字
\s匹配空白,即空格,tab键
\S匹配非空白
\w匹配单词字符,即 a-z、A-Z、0-9、_
\W匹配非单词字符
  • 数量匹配
字符功能
*匹配前一个规则的字符出现0至无限次
+匹配前一个规则的字符出现1至无限次
?匹配前一个规则的字符出现0次或1次
{m}匹配前一个规则的字符出现m次
{m,}匹配前一个规则的字符出现最少m次
{m,n}匹配前一个规则的字符出现m到n次
  • 边界匹配
字符功能
^匹配字符串开头
$匹配字符串结尾
\b匹配一个单词的边界
\B匹配非单词边界
  • 分组匹配
字符功能
|匹配左右任意一个表达式
( )将括号中字符作为一个分组
\b匹配一个单词的边界
\B匹配非单词边界

案例一:找出字符串中所有数字

import re
s = "test@@1452282!!###python"
# 找出全部数字
result = re.findall(r'\d',s)    #字符串前带上r的标记,表示字符串中转义字符无效,就是普通字符
print(result)

结果为:
['1', '4', '5', '2', '2', '8', '2']

案例二:找出字符串中所有特殊字符

import re
s = "test@@1452282!!###python"
# 找出全部特殊字符
result = re.findall(r'\W',s)    #字符串前带上r的标记,表示字符串中转义字符无效,就是普通字符
print(result)

结果为:
['@', '@', '!', '!', '#', '#', '#']

案例三:找出字符串中所有英文字母

import re
s = "test@@1452282!!###python"
# 找出全部英文字母
result = re.findall(r'[a-zA-Z]',s)    #字符串前带上r的标记,表示字符串中转义字符无效,就是普通字符
print(result)

结果为:
['t', 'e', 's', 't', 'p', 'y', 't', 'h', 'o', 'n']

案例四:匹配字符串中只能包含字母和数字,并且长度为6到18位

import re
r = "^[0-9a-zA-Z]{6,18}$"
result = re.findall(r,"123adalg")
result2 = re.findall(r,"laeooeoaoao11111111oa44252")
print(result)
print(result2)

结果为:
['123adalg']
[]

案例五:匹配字符串为纯数字,长度5到11位,第一位不为0

import re
r = "^[1-9][0-9]{4,10}$"
result = re.findall(r,"012345")
result2 = re.findall(r,"12355")
print(result)
print(result2)

结果为:
[]
['12355']

案例六:匹配字符串的邮箱地址,只允许qq、163、gmail这三种邮箱地址

import re
r = r'(^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+$)'
result = re.match(r,"12346@162.com")
result2 = re.match(r,"12346@163.com")
print(result)
print(result2.group())

结果为:
None
12346@163.com

7、递归

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

案例:使用递归设计一个函数,获取一个路径下的所有文件名

import os
def get_all_files(path):
    """
    获取指定路径下的所有文件
    :param path: 被判断的文件夹
    :return: list,包含全部的文件,如果目录不存在或者无文件则返回一个空list
    """
    file_list = []
    if os.path.exists(path):
        for f in os.listdir(path):      # 循环路径下的所有文件和文件夹
            new_path = path + "/" + f
            if os.path.isdir(new_path):    # 判断是否为文件夹
                file_list += get_all_files(new_path)   # 递归调用
            else:
                file_list.append(new_path)


    else:
        print("路径不存在")
        return []

    return file_list

http://www.kler.cn/news/364026.html

相关文章:

  • ASP.NET Core 8.0 中使用 Hangfire 调度 API
  • javaWeb项目-ssm+jsp大学生校园兼职系统功能介绍
  • 如何使用Golang的gomail库实现邮件发送功能
  • 基于Multisim的水位测量电路设计与仿真
  • 比较相同机器上 redis和mysql分别单独承载的 最大连接数量
  • iOS--利用UITableViewDataSourcePrefetching实现平滑如丝的无限滚动
  • centos-LAMP搭建与配置(论坛网站)
  • 20241024拿掉飞凌OK3588-C的开发板linux R4启动时的LOGO
  • NSSCTF
  • 大话红黑树之(2)源码讲解TreeMap-Java
  • 探索人工智能在自然语言处理中的应用
  • JVM学习之路(3)类加载器
  • 通过 Lighthouse 和 speed-measure-webpack 插件分析优化构建速度与体积
  • 算法通关--单调栈
  • 无废话、光速上手 React-Router
  • Anaconda从旧版本更新
  • 用nginx实现多ip访问多网址
  • Redis缓存实战-使用Spring AOP和Redis实现Dao层查询缓存优化实战
  • 云原生后端概述
  • RabbitMQ 确认模式(Acknowledgements Mode)详解
  • 海外媒体发稿:外媒宣发之《时代》杂志 TIME 的魅力
  • 使用 NumPy 和 Matplotlib 实现交互式数据可视化
  • 【Android】AHandler/AMessage/ALooper机制
  • Java:关于哈希表
  • 2024年808数据结构答案
  • css知识点梳理