文章目录
- 第三章:
- 3.1 闭包
- 3.2 装饰器
- 语法糖写法
- 3.3 设计模式
- 3.3.1 单例模式
- 3.3.2 工厂模式
- 3.4 多线程
- 3.4.1 进程、线程和并行执行
- 3.4.2 多线程编程
- 3.5 网络编程
- 3.5.1 Socket
- 3.5.2 服务端开发
- 3.5.3 客户端开发
- 3.6 正则表达式
- 3.6.1 基础匹配
- 3.6.2 元字符匹配
- 单字符匹配:
- 元字符匹配:
- 边界匹配:
- 分组匹配:
- 3.7 递归
第三章:
3.1 闭包
在函数嵌套前提下,内部函数使用了外部函数的变量,并外部函数返回内部函数,把这个使用外部函数变量的内部函数称为闭包
def outer(logo):
def inner(msg):
print(f"<logo>{msg}<logo>")
return inner
fn1 = outer("Hello")
fn1("world")
fn1("World")
fn2 = outer("Python")
fn2("hello")
fn2("Hello")
例:
def account_Create(amount_Init=0):
def atm(num, deposit=True):
nonlocal amount_Init
if deposit:
amount_Init += num
print(f"存款+{num},账户余额{amount_Init}")
else:
amount_Init -= num
print(f"存款-{num},账户余额{amount_Init}")
return atm
fn = account_Create()
fn(100)
fn(300, True)
fn(200, False)
优点:
-
无需定义全局变量就可实现通过函数,持续访问,修改某个值
-
闭包使用的变量所用在函数内,难以被错误的调用修改
缺点:
- 内部函数持续引用外部函数的值,以至于这一部分内存空间不被释放,一直占用内存
在闭包函数中想要修改外部函数的变量值,需用nonlocal声明该外部变量
3.2 装饰器
作用:不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能
def outer(func):
def inner():
print("11111")
func()
print("22222")
return inner
def sleep():
import random
import time
print("睡觉Zzzz")
time.sleep(random.randint(1,3))
fn = outer(sleep)
fn()
语法糖写法
def outer(func):
def inner():
print("11111")
func()
print("22222")
return inner
@outer
def sleep():
import random
import time
print("睡觉Zzzz")
time.sleep(random.randint(1,3))
sleep()
3.3 设计模式
除了面向对象外,在编程中有很多既定的套路可方便开发,称为设计模式
-
单例、工厂模式
-
建造者、状态、备忘录、访问者、模板、代理模式
-
等模式
3.3.1 单例模式
作用:确保某一个类只有一个实例存在
保证一个类只有一个实例,并提供一个访问的全局访问点。
# test02
class strTools:
pass
str_tool = strTools()
# test
from test02 import str_tool
s1 = str_tool
s2 = str_tool
print(s1)
print(s2)
"""
<test02.strTools object at 0x000001BFBED43EF0>
<test02.strTools object at 0x000001BFBED43EF0>
"""
3.3.2 工厂模式
当需要大量创建一个类的实例时,可使用工厂模式。从原生的使用类去构造去创建对象的形式,迁移到,基于工厂提供的方法去创建对象的形式。
class Person:
pass
class Worker(Person):
pass
class Student(Person):
pass
class Teacher(Person):
pass
class Factory:
def get_person(self,person_Type):
if person_Type == 'w':
return Worker()
elif person_Type == 's':
return Student()
else:
return Teacher()
factory = Factory()
worker = factory.get_person('w')
student = factory.get_person('s')
teacher = factory.get_person('t')
-
大批量创建对象时有统一的入口,易于代码维护
-
当发生变化时,只需修改工厂类的创建方法即可
-
符合现实世界的模式,由工厂来制作产品(对象)
3.4 多线程
3.4.1 进程、线程和并行执行
进程:一个程序,运行在系统之上,那便称该程序为一个运行进程,并分配进程ID方便管理系统
线程:线程归属于进程,一个进程可开启多个线程,执行不同的工作,是进程的实际工作最小单位
多任务运行:操作系统中可运行多个进程
多线程运行:一个进程内可运行多个线程
进程之间是内存隔离的,不同的进程拥有各自的内存空间。
线程之间是内存共享的,线程是属于进程的,一个进程内的多个线程之间是共享该进程所拥有的内存空间
并行执行:同一时间做不同的工作
多任务同时运行,即不同的程序同时运行,称为:多任务并行执行
一个程序在同一时间做两件或更多件不同的事情时,称为:多线程并行执行
3.4.2 多线程编程
通过threading模块实现
import threading
thread_obj = threading.Thread([group [, target, [, name[, args[, kwargs]]]]])
thread_obj.start() # 启动线程,让线程开始工作
-
group:未来功能的预留参数
-
target:执行的目标任务名
-
args:以元组方式给执行任务传参
-
kwargs:以字典方式给执行任务传参
-
name:线程名
import threading
import time
def Coffee():
while True:
print("喝咖啡")
time.sleep(1)
def Tea():
while True:
print("喝茶")
time.sleep(1)
# 创建进程
coffee_thread = threading.Thread(target=Coffee)
tea_thread = threading.Thread(target=Tea)
# 执行进程
coffee_thread.start()
tea_thread.start()
import threading
import time
def Coffee(msg):
while True:
print(msg)
time.sleep(1)
def Tea(msg):
while True:
print(msg)
time.sleep(1)
# 创建进程
coffee_thread = threading.Thread(target=Coffee, args=("喝咖啡"))
tea_thread = threading.Thread(target=Tea, args={"msg": "喝茶"})
# 执行进程
coffee_thread.start()
tea_thread.start()
3.5 网络编程
3.5.1 Socket
socket:进程之间通讯的一个工具,进程之间想要进行网络通信需要socket
socket负责进程之间的网络数据传输
2个进程之间通过Socket进行相互通信,必须有服务端和客户端
Socket服务端:等待其他进程的连接、可接收发来的信息、可回复信息
Socket客户端:主动连接服务器、可发送信息、可接收回复
3.5.2 服务端开发
步骤:
1.创建socket对象
2.绑定socket_server到指定IP地址和端口
3.服务端开始监听端口
4.接收客户端连接,获取链接对象
5.连接客户端后,通过recv方法,接收客户端所发来的信息
6.通过conn,调用send方法可回复消息
7.conn和socket_server对象调用close方法,关闭连接
import socket # 导包
socket_server = socket.socket() # 创建socket对象
socket_server.bind(("localhost", 8888)) # 绑定IP地址和端口
socket_server.listen(1) # 监听端口
# listen方法内接收一个整数传参数,表示接收的链接数量
conn, address = socket_server.accept() # 接收客户端连接,获取连接对象
"""
result: tuple = socket_server.accept()
conn = result[0] # 客户端和服务端的连接对象
address = result[1]# 客户端的地址信息
accept方法返回的是二元元组(连接对象,客户端地址信息)
可通过变量1,变量2 = socket_server.accept()的形式,直接接收二元元组内的两个元素
accept()方法,是阻塞的方法,等待客户端的连接,若没有连接,卡着不执行了
"""
print(f"接收到客户端的连接,信息是:{address}")
data: str = conn.recv(1024).decode("UTF-8") # 连接客户端后,通过recv方法,接收客户端所发来的信息
""""
接收客户端信息,使用客户端和服务端的本次连接对象,而非socket_server对象
recv接收的参数是缓冲区大小,一般1024
recv方法的返回值是一个字节数组也是Bytes对象,不是字符串,可通过decode方法UTF-8编码,将字节数组转换为字符串对象
"""
print(f"客户端发来的信息是:{data}")
msg = input("请输入和客户端回复的信息:").encode("UTF-8") # 发送回复信息
conn.send(msg) # 通过conn,调用send方法可回复消息
conn.close() # conn和socket_server对象调用close方法,关闭连接
socket_server.close()
优化:
import socket
socket_server = socket.socket()
socket_server.bind(("localhost", 8888))
socket_server.listen(1)
conn, address = socket_server.accept()
print(f"接收到客户端的连接,来的{address}")
while True:
data = conn.recv(1024).decode("UTF-8")
if data == 'exit':
break
print(f"发送来的信息是:{data}")
reply = input("请输入回复的信息:").encode("UTF-8")
conn.send(reply)
conn.close()
socket_server.close()
3.5.3 客户端开发
步骤:
1.创建socket对象
2.连接到服务端
3.发送消息
4.接收并返回消息
5.关闭连接
import socket # 导包
socket_client = socket.socket() # 创建socket对象
socket_client.connect(("localhost", 8888)) # 连接到服务器
socket_client.send("你好".encode("UTF-8")) # 发送消息
data = socket_client.recv(1024) # 接收信息并返回
print(f"服务端返回的信息为:{data}") #
socket_client.close() # 关闭连接
3.6 正则表达式
正则表达式又称规则表达式,是使用单个字符串来描述、匹配某个句法规则的字符串,常被用来检索、替换哪些符号某个模式的文本。
使用:字符串定义规则,并通过规则去验证字符串是否匹配
3.6.1 基础匹配
三个基础方法:
<1>match
<2>search
<3>findall
- re.match(匹配规则,被匹配字符串)
从被匹配字符串开头进行匹配,匹配成功返回匹配对象,不成功则返回空
import re
str = "hello python"
ret = re.match("hello", str)
print(ret) # <re.Match object; span=(0, 5), match='hello'>
print(ret.span()) # (0, 5)
print(ret.group()) # hello
str2 = "1hello python"
ret2 = re.match("hello", str2)
print(ret2) # None
print(ret2.span())
print(ret2.group())
- search(匹配规则,被匹配字符串)
搜索整个字符串,找出匹配的字符串,从前往后,找到第一个后就停止,不会继续向后
import re
str = "hello python"
ret = re.search("python", str)
print(ret) # <re.Match object; span=(6, 12), match='python'>
print(ret.span()) # (6, 12)
print(ret.group()) # python
str2 = "hello 1python"
ret2 = re.search("python", str2)
print(ret2) # None # <re.Match object; span=(7, 13), match='python'>
print(ret2.span()) # (7, 13)
print(ret2.group()) # python
- findall(匹配规则,被匹配字符串)
匹配整个字符串,找出全部匹配项,找不到返回空list:[]
import re
str = "hello python"
ret = re.findall("python", str)
print(ret) # ['python']
str2 = "hello 1python"
ret2 = re.search("world", str2)
print(ret2) # None
3.6.2 元字符匹配
单字符匹配:
字符 | 功能 |
---|---|
. | 匹配任意1个字符,除了\n |
[] | 匹配[]内列举的字符 |
\d | 匹配数字,0-9 |
\D | 匹配非数字 |
\s | 匹配空白,空格、tab键 |
\S | 匹配非空白 |
\w | 匹配单词字符,a-z、A-Z 、0-9、_ |
\W | 匹配非单词字符 |
import re
str = "hello 2python"
ret = re.findall(r'[e-h]', str) # 字符串前加上r标记,表示字符串中转义字符无效,为普通字符
print(ret) # ['h', 'e', 'h']
元字符匹配:
字符 | 功能 |
---|---|
* | 匹配前一个规则的字符出现0次至无数次 |
+ | 匹配前一个规则的字符出现1次至无数次 |
? | 匹配前一个规则的字符出现0次或1次 |
{m} | 匹配前一个规则的字符出现m次 |
{m,} | 匹配前一个规则的字符出现至少m次 |
{m, n} | 匹配前一个规则的字符出现m至n次 |
边界匹配:
字符 | 功能 |
---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
\b | 匹配一个单词的边界 |
\B | 匹配非单词边界 |
分组匹配:
字符 | 功能 |
---|---|
() | 括号中字符作为一个分组 |
import re
# 1
r = '^[0-9a-zA-Z]{6,10}$' # 只能由字母和数字组成,长度范围6-10位
str = '123456abc'
print(re.findall(r, str)) # ['123456abc']
# 2 纯数字,长度5-10位,第一位不可为0
r = '^[1-9][0-9]{5,9}$'
str = '12345678'
print(re.findall(r, str)) # ['12345678']
# 3 匹配邮箱地址,只允许168、qq邮箱地址
r = r'(^[\w-]+(\.[\w-]+)*@(163|qq)(\.[\w-]+)+$)'
str = 'ziqi@163.com'
print(re.findall(r, str)) # [('ziqi@163.com', '', '163', '.com')]
3.7 递归
方法/函数自己调用自己的一种特殊编程写法
def func():
if 1:
func()
return 0
import os
def test_os():
print(os.listdir("D:/test")) # 列出路径下的内容
print(os.path.isdir("D:/test/a")) # 判断指定路径是不是文件夹
print(os.path.exists("D:/test")) # 判断指定路径是否存在
def get_files(path):
"""
从指定的文件中用递归的方式,获取全部的文件列表
:param path: 被判断的文件夹
:return: 包含全部文件,若目录不存在或无文件则返回一个空list[]
"""
file_list = []
if os.path.exists(path):
for f in os.listdir(path):
new_path = path + "/" + f
if os.path.isdir(new_path):
get_files(new_path)
else:
file_list.append(new_path)
else:
print(f"指定目录{path}不存在")
return []
print(get_files("D:/test"))