Python学习7
PySpark
Spark 是Apache基金会旗下的顶级开源项目,用于对海量数据进行大规模分布式计算
Spark对Python语言的支持,重点体现在,Python第三方库:PySpark之上。PySpark不仅可以作为Python第三方库使用,也可以将程序提交的Spark集群环境中,调度大规模集群进行执行。
""" 演示获取PySpark的执行环境入库对象:SparkContext 并通过SparkContext对象获取当前PySpark的版本 """ # 导包 from pyspark import SparkConf,SparkContext # 创建SparkConf 类对象 conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app") # 基于SparkConf类对象创建SparkContext对象 sc = SparkContext(conf = conf) # 打印PySpark的运行版本 print(sc.version) # 停止SparkContext对象的运行(停止PySpark程序) sc.stop()
SparkContext类对象,是PySpark编程中一切功能的入口。
PySpark的编程,主要分为三步:
通过SparkContext对象,完成数据输入; 输入数据后得到RDD对象,对RDD对象进行迭代计算; 最终通过RDD对象的成员方法,完成数据输出工作
- RDD对象
PySpark支持多种数据的输入,在输入完成后,都会得到一个:RDD类的对象。 RDD全称为:弹性分布式数据集
PySpark针对数据的处理,都是以RDD对象作为载体,即:
数据存储在RDD内; 各类数据的计算方法,也都是RDD的成员方法; RDD的数据计算方法,返回值依旧是RDD对象
Python数据容器转RDD对象:
PySpark 支持通过SparkContext对象的Parallelize成员方法,将:list,tuple,set,dict,str转换为PySpark的RDD对象
注意:字符串会被拆分出1个个的字符,存入RDD对象; 字典仅有key会被存入RDD对象
- 通过SparkContext的parallelize成员方法,将Python数据容器转换为RDD对象
""" 演示通过PySpark代码加载数据,即数据输入 """ # 导包 from pyspark import SparkConf,SparkContext # 创建SparkConf 类对象 conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app") # 基于SparkConf类对象创建SparkContext对象 sc = SparkContext(conf = conf) # 通过parallelize方法将Python对象加载到Spark内,成为RDD对象 rdd1 = sc.parallelize([1, 2, 3, 4, 5]) rdd2 = sc.parallelize((1, 2, 3, 4, 5)) rdd3 = sc.parallelize("abcdefg") rdd4 = sc.parallelize({1, 2, 3, 4, 5}) rdd5 = sc.parallelize({"key1": "value1", "key2": "value2"}) # 查看RDD里面有什么内容,需要用collect()方法 print(rdd1.collect()) # 结果是:[1, 2, 3, 4, 5] print(rdd2.collect()) # 结果是:[1, 2, 3, 4, 5] sc.stop()
- 通过SparkContext的textFile成员方法,读取文本文件得到RDD对象
""" 演示通过PySpark代码加载数据,即数据输入4 """ # 导包 from pyspark import SparkConf,SparkContext # 创建SparkConf 类对象 conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app") # 基于SparkConf类对象创建SparkContext对象 sc = SparkContext(conf = conf) # 用textFile方法,读取文件数据加载到Spark内,成为RDD对象 rdd = sc.textFile("测试.txt") print(rdd.collect()) sc.stop()
数据计算
PySpark的数据计算,都是基于RDD对象来进行的。RDD对象内置丰富的:成员方法(算子)
- map算子
功能:map算子,是将RDD的数据一条条处理(处理的逻辑 基于map算子中接收的处理函数),返回新的RDD
""" 演示RDD的map成员方法的使用 """ # 导包 from pyspark import SparkConf,SparkContext import os os.environ['PYSPARK_PYTHON'] = "D:\Anaconda3\python.exe" # 创建SparkConf 类对象 conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app") # 基于SparkConf类对象创建SparkContext对象 sc = SparkContext(conf = conf) # 用textFile方法,读取文件数据加载到Spark内,成为RDD对象 rdd = sc.parallelize([1, 2, 3, 4, 5]) # 通过map方法将全部数据乘以10 def func(data): return data*10 rdd2 = rdd.map(func) print(rdd2.collect())
- map链式调用
对于返回值是新的RDD的算子,可以通过链式调用的方式多次调用算子
# 导包 from pyspark import SparkConf,SparkContext import os os.environ['PYSPARK_PYTHON'] = "D:\Anaconda3\python.exe" conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app") sc = SparkContext(conf = conf) # 用textFile方法,读取文件数据加载到Spark内,成为RDD对象 rdd = sc.parallelize([1, 2, 3, 4, 5]) # 链式调用 rdd2 = rdd.map(lambda x: x * 10).map(lambda x: x + 5) print(rdd2.collect()) # 结果是: [15, 25, 35, 45, 55]
- flatMap算子
功能:对rdd执行map操作,然后进行解除嵌套操作
# 导包 from pyspark import SparkConf,SparkContext import os os.environ['PYSPARK_PYTHON'] = "D:\Anaconda3\python.exe" conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app") sc = SparkContext(conf = conf) # 用textFile方法,读取文件数据加载到Spark内,成为RDD对象 rdd = sc.parallelize(["tomorrow is a guud day!", "today is guud", "and yesterday"]) # 需求,将RDD数据里面的单词一个个提取出来 rdd2 = rdd.flatMap(lambda x: x.split(" ")) print(rdd2.collect()) # 结果是:['tomorrow', 'is', 'a', 'guud', 'day!', 'today', 'is', 'guud', 'and', 'yesterday']
- reduceByKey算子
功能:针对KV型RDD(二元元组),自动按照Key进行分组,然后根据你提供的聚合逻辑,完成组内数据(value)的聚合操作
语法:
注意: reduceByKey中接收的函数,只负责聚合,不理会分组;分组是by key 来自动分组的
# 导包 from pyspark import SparkConf,SparkContext import os os.environ['PYSPARK_PYTHON'] = "D:\Anaconda3\python.exe" conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app") sc = SparkContext(conf = conf) rdd = sc.parallelize([('男', 97), ('男', 76), ('女', 95), ('女', 89)]) # 求男生和女生两个组的成绩之和 rdd2 = rdd.reduceByKey(lambda a, b: a + b) print(rdd2.collect()) # 结果是:[('男', 173), ('女', 184)]
- Filter算子
功能:过滤 对想要的数据进行保留
语法:
注意:filter算子,接收一个处理函数,可用lambda快速编写; 函数对RDD数据逐个处理,得到True的保留并返回到RDD中
from pyspark import SparkContext, SparkConf import os os.environ['PYSPARK_PYTHON'] = "D:\Anaconda3\python.exe" conf = SparkConf().setMaster("local[*]").setAppName("test_spark") sc = SparkContext(conf = conf) rdd = sc.parallelize([1, 2, 3, 4, 5]) # 对RDD的数据进行过滤 rdd2 = rdd.filter(lambda num: num % 2 == 0) # 得到true的保留在rdd2中 print(rdd2.collect())
- distinct算子
功能:对RDD数据进行去重,返回新RDD
语法:rdd.distinct( ) 无需传参
from pyspark import SparkContext, SparkConf import os os.environ['PYSPARK_PYTHON'] = "D:\Anaconda3\python.exe" conf = SparkConf().setMaster("local[*]").setAppName("test_spark") sc = SparkContext(conf = conf) rdd = sc.parallelize([1, 2, 3, 4, 5, 6, 7, 7, 4, 2, 9]) # 对RDD的数据进行去重 rdd2 = rdd.distinct() print(rdd2.collect()) # 结果是:[1, 9, 2, 3, 4, 5, 6, 7]
- sortBy算子
功能:对RDD数据进行排序,基于你指定的排序依据
语法:
注意:全局进行排序要设置分区数为:1
from pyspark import SparkContext, SparkConf import os os.environ['PYSPARK_PYTHON'] = "D:\Anaconda3\python.exe" conf = SparkConf().setMaster("local[*]").setAppName("test_spark") sc = SparkContext(conf = conf) rdd = sc.textFile("测试.txt") word_rdd = rdd.flatMap(lambda x: x.split(" ")) word_with_one_rdd = word_rdd.map(lambda word:(word, 1)) result_rdd = word_with_one_rdd.reduceByKey(lambda a, b: a + b) # 对结果进行排序 final_rdd = result_rdd.sortBy(lambda x: x[1], ascending= False, numPartitions= 1) print(final_rdd.collect())
数据输出
数据输出的方法:collect:将RDD内容转换为list, reduce:对RDD内容进行自定义聚合,
take:取出RDD的前N个元素组成list,count:统计RDD元素个数
- collect算子
功能:将RDD各个分区内的数据,统一收集到Driver中,形成一个List对象
用法: rdd.collect( ) 返回值是一个list
from pyspark import SparkConf, SparkContext import os import json os.environ['PYSPARK_PYTHON'] = 'D:\Anaconda3\python.exe' conf = SparkConf().setMaster("local[*]").setAppName("test_spark") sc = SparkContext(conf = conf) # 准备RDD rdd = sc.parallelize([1, 2, 3, 4, 5]) # collect 算子,输出RDD为list对象 rdd_list:list = rdd.collect() print(rdd_list) # 结果:[1, 2, 3, 4, 5] print(type(rdd_list)) # 结果:<class 'list'>
- reduce算子
功能:对RDD数据集按照传入的逻辑进行聚合
语法:
from pyspark import SparkConf, SparkContext import os import json os.environ['PYSPARK_PYTHON'] = 'D:\Anaconda3\python.exe' conf = SparkConf().setMaster("local[*]").setAppName("test_spark") sc = SparkContext(conf = conf) # 准备RDD rdd = sc.parallelize([1, 2, 3, 4, 5]) # reduce 算子,对RDD进行两两聚合 num = rdd.reduce(lambda a, b:a + b) print(num) # 结果:15
- take算子
功能: 取RDD的前N个元素,组合成list返回
from pyspark import SparkConf, SparkContext import os import json os.environ['PYSPARK_PYTHON'] = 'D:\Anaconda3\python.exe' conf = SparkConf().setMaster("local[*]").setAppName("test_spark") sc = SparkContext(conf = conf) # 准备RDD rdd = sc.parallelize([1, 2, 3, 4, 5]) # take算子, 取出RDD的前N个元素,组成list返回 take_list = rdd.take(3) print(take_list) # 结果:[1, 2, 3]
- count算子
功能:计算RDD内有多少条数据,返回一个数值
from pyspark import SparkConf, SparkContext import os import json os.environ['PYSPARK_PYTHON'] = 'D:\Anaconda3\python.exe' conf = SparkConf().setMaster("local[*]").setAppName("test_spark") sc = SparkContext(conf = conf) # 准备RDD rdd = sc.parallelize([1, 2, 3, 4, 5]) # count算子,统计RDD内有多少条数据,返回值为数字 num_count = rdd.count() print(f"rdd内有{num_count}个元素") # 结果:rdd内有5个元素
数据输出到文件
- saveAsTextFile算子
功能:将RDD的数据写入文本文件中; 支持本地写出,hdfs等文件系统
代码:
rdd = sc.parallelize([1, 2, 3, 4, 5])
rdd.saveAsTextFile("想写入的文件路径")
- 修改rdd为1个分区
方式一:SparkConf对象设置属性全局并行度为1
conf = SparkConf().setMaster("local[*]").setAppName("test_spark") conf.set("spark.default.parallelism","1") sc = SparkContext(conf =conf)
方式二:创建RDD的时候设置(parallelize方法传入numSlices参数为1)
rdd = sc.parallelize([1, 2, 3, 4, 5], numSlices=1)
rdd1 = sc.parallelize([1, 2, 3, 4, 5], 1)
from pyspark import SparkConf, SparkContext import os import json os.environ['PYSPARK_PYTHON'] = 'D:\Anaconda3\python.exe' os.environ['HADOOP_HOME'] = 'D:\hadoop-3.0.0\hadoop-3.0.0' conf = SparkConf().setMaster("local[*]").setAppName("test_spark") conf.set("spark.default.parallelism", "1") # 按照方式一,分区为1 sc = SparkContext(conf = conf) # 准备RDD rdd = sc.parallelize([1, 2, 3, 4, 5]) rdd2 = sc.parallelize([("hello", 3), ("Spark", 5), ("Hi", 7)]) rdd3 = sc.parallelize([[1, 3, 5], [6, 7, 9], [11, 13, 14]]) # 输出到文件中 rdd.saveAsTextFile("D:\PyCharm Community Edition 2022.3.3\PycharmProjects\output1") rdd2.saveAsTextFile("D:\PyCharm Community Edition 2022.3.3\PycharmProjects\output2") rdd3.saveAsTextFile("D:\PyCharm Community Edition 2022.3.3\PycharmProjects\output3")
from pyspark import SparkConf, SparkContext import os import json os.environ['PYSPARK_PYTHON'] = 'D:\Anaconda3\python.exe' os.environ['HADOOP_HOME'] = 'D:\hadoop-3.0.0\hadoop-3.0.0' conf = SparkConf().setMaster("local[*]").setAppName("test_spark") sc = SparkContext(conf = conf) # 准备RDD 按照方式二设置分区为1 rdd = sc.parallelize([1, 2, 3, 4, 5], numSlices=1) rdd2 = sc.parallelize([("hello", 3), ("Spark", 5), ("Hi", 7)],1) rdd3 = sc.parallelize([[1, 3, 5], [6, 7, 9], [11, 13, 14]],1) # 输出到文件中 rdd.saveAsTextFile("D:\PyCharm Community Edition 2022.3.3\PycharmProjects\output1") rdd2.saveAsTextFile("D:\PyCharm Community Edition 2022.3.3\PycharmProjects\output2") rdd3.saveAsTextFile("D:\PyCharm Community Edition 2022.3.3\PycharmProjects\output3")
闭包
定义双层嵌套函数,内层函数可以访问外层函数的变量;将内层函数作为外层函数的返回,次内层函数就是闭包函数
优点:
无需定义全局变量即可实现通过函数,持续访问、修改某个值。 闭包使用的变量的作用域在函数内,难以被错误的调用修改
缺点:
由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存。
""" 演示Python的闭包特性 """ # 简单闭包 def outer(logo): def inner(msg): print(f"<{logo}>{msg}<{logo}>") return inner fn1 = outer("你好") # 调用outer返回的是一个函数fn1 fn1("大家好") # 通过上面返回的fn1函数 结果;<你好>大家好<你好> fn2 = outer("天气好") # 调用outer返回的是一个函数fn2 fn2("出来玩") # 通过上面返回的fn2函数 结果;<天气好>出来玩<天气好>
- 修改外部函数变量的值
需要使用nonlocal关键字修饰外部函数的变量,才可在内部函数中修改它
def outer(num1): def inner(num2): nonlocal num1 num1 += num2 print(num1) return inner fn = outer(10) fn(10) # 结果:20 fn(10) # 结果:30 fn(10) # 结果:40
案例:
# 使用闭包实现ATM小案例 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(100) # 结果:存款:+100, 账户余额:100 atm(200) # 结果:存款:+200, 账户余额:300 atm(100,deposit=False) # 结果:取款:-100, 账户余额:200
装饰器
装饰器就是使用创建一个闭包函数,在闭包函数内调用目标函数,可以达到不改动目标函数的同时,增加额外的功能。
- 简单装饰器写法:
# 演示装饰器的一般写法(闭包): def outer(func): def inner(): print("我睡觉了!") func() print("我起床啦") return inner def sleep(): import random import time print("睡眠中.......") time.sleep(random.randint(1,5)) fn = outer(sleep) fn()
- 装饰器的语法糖写法:
使用@outer 定义在目标函数sleep之上
# 演示装饰器的快捷写法(语法糖): 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()
设计模式
设计模式是一种编程套路,可以极大的方便程序的开发。最常见、最经典的设计模式,就是我们所学习的面向对象
除了面向对象外,在编程中也有很多既定的套路可以方便开发,我们称之为设计模式:
- 单例、工厂模式
- 建造者、责任链、状态、备忘录、解释器、访问者、观察者、中介、模板、代理模式
- 等等模式
单例模式
创建类的实例后,就可以得到一个完整的、独立的类对象。可以节省内存、节省创建对象的开销。
class Tool: pass t1 = Tool() t2 = Tool() print(t1) # 结果:<__main__.Tool object at 0x000001908931A460> print(t2) # 结果:<__main__.Tool object at 0x000001908938C280>
通过print()语句可以看出,它们的内存地址是不相同的,即t1和t2是完全独立的两个对象。
单例模式是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。
定义: 保证一个类只有一个实例,并提供一个访问它的全局访问点
使用场景:当一个类只能有一个实例,而客户可以从一个众所周知的访问点访问它时。
工厂模式
当需要大量创建一个类的实例的时候,可以使用工厂模式。即,从原生的使用类的构造去创建对象的形式迁移到,基于工厂提供的方法去创建对象的形式
好处:大批量创建对象的时候有统一的入口,易于代码维护; 当发生修改,仅修改工厂类的创建方法即可; 符合现实世界的模式,即由工厂来制作产品
# 演示设计模式之工厂模式 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方便系统管理。
线程:线程是属于进程的,一个进程可以开启多个线程,执行不同的工作,是进程的实际工作最小单位
进程就好比一家公司,是操作系统对程序进行运行管理的单位。线程就好比公司的员工,进程可以有多个线程(员工),是进程实际的工作者。
操作系统中可以运行多个进程,即多任务运行。一个进程内可以运行多个线程,即多线程运行。
进程之间是内存隔离的,即不同的进程拥有各自的内存空间。这就类似于不同的公司拥有不同的办公场所。
线程之间是内存共享的,线程是属于进程的,一个进程内的多个线程之间是共享这个进程所拥有的内存空间的。这就好比,公司员工之间是共享公司的办公场所。
并行执行
并行执行的意思指的是同一时间做不同的工作。进程之间就是并行执行的,操作系统可以同时运行好多程序,这些程序都是在并行执行。
处理进程外,线程其实也是可以并行执行的。也就是比如一个Python程序,其实是完全可以做到:
一个线程在输出:你好; 一个线程在输出:Hello
像这样一个程序再同一时间做两件乃至多件不同的事情,我们就称之为:多线程并行执行
threading模块
绝大数编程语言,都允许多线程编程,Python也不例外。Python的多线程可以通过threading模块来实现。
# 演示多线程编程的使用 import time import threading def sing(): print("我在唱歌,啦啦啦啦....") time.sleep(1) def dance(): print("我在跳舞,哗哗哗哗....") time.sleep(1) if __name__ == '__main__': # 创建一个唱歌的线程 sing_thread = threading.Thread(target=sing) # 创建一个跳舞的线程 dance_thread = threading.Thread(target=dance) # 让线程去干活吧: sing_thread.start() dance_thread.start()
需要传参的话可以通过:
- args参数通过元组(按参数顺序)的方式传参
- 使用kwargs参数用字典的形式传参
# 演示多线程编程的使用 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负责进程之间的网络数据传输,好比数据的搬运工。
2个进程之间通过Socket进行相互通讯,就必须有服务端和客户端
Socket服务端:等待其它进程的连接,可接受发来的消息、可以回复消息
Socket客户端:主动连接服务端、可以发送消息、可以接收回复
- Socket服务端编程
1.创建socket对象
import socket socket_server = socket.socket()
2.绑定socket_server到指定IP和地址
socket_server.bind(host, port)
3.服务端开始监听端口
socket_server.listen(backlog) # backlog为int整数,表示允许的连接数量,超出的会等待,可以不填,不填会自动设置一个合理值
4.接受客户端连接,获得连接对象
conn, address = socket_server.accept() print(f"接收到客户端连接,连接来自{address}") # accept方法是阻塞方法,如果没有连接,就会卡在当前这一行不向下执行代码 # accept返回的是一个二元元组,可以使用上述形式,用两个变量接受二元元组的2个元素
5.客户端连接后,通过recv方法,接收客户端发送的消息
while True: data = conn.recv(1024).decode("UTF-8") # recv方法的返回值是字节数组(Bytes),可以通过decode使用UTF_8解码为字符串 # recv方法的传参是buffsize,缓冲区大小,一般设置为1024即可 if data == 'exit': break print("接收到发送来的数据:", data) # 可以通过while True无限循环来保持和客户端进行数据交互 # 可以通过判定客户端发来的特殊标记,如exit,来退出无限循环
6.通过conn(客户端当次连接对象),调用send方法可以回复消息
while True: data = conn.recv(1024).decode("UTF-8") if data == 'exit': break print("接收到发送来的数据:", data) conn.send("你好呀嘿嘿".encode("UTF-8"))
7.conn(客户端当次连接对象)和socket_server对象调用close方法,关闭连接
conn.close()
socket_server.close()
整体:
import socket # 创建Socket对象 socket_server = socket.socket() # 绑定IP地址和端口 socket_server.bind("localhost", 8888) # 监听端口 socket_server.listen(1) # listen方法内接收一个整数传参数,便是接收的连接数量 # 等待客户端连接 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方法的返回值是字节数组(Bytes),可以通过decode使用UTF_8解码为字符串 # recv方法的传参是buffsize,缓冲区大小,一般设置为1024即可 print(f"客户端发来的消息是:{data}") # 发送回复消息 msg = input("请输入你要和客户端回复的消息:") if msg == 'exit': break conn.send(msg.encode("UTF-8")) # 可以通过while True无限循环来保持和客户端进行数据交互 # 可以通过判定客户端发来的特殊标记,如exit,来退出无限循环 while True: data = conn.recv(1024).decode("UTF-8") if data == 'exit': break print("接收到发送来的数据:", data) conn.send("你好呀嘿嘿".encode("UTF-8")) # 关闭连接 conn.close() socket_server.close()
- socket客户端编程
1.创建socket对象
import socket socket_client = socket.socket()
2.连接到服务端
socket_client.connect("localhost", 8888)
3.发送消息
while True: # 可以通过无限循环来保证持续的发送消息给服务端 send_msg = input("请输入要发送的消息:") if send_msg == 'exit': # 通过特殊标记来确保可以退出无限循环 break socket_client.send(send_msg.encode("UTF-8")) #消息需要编码为字节数组(UTF-8编码)
4.接收返回消息
while True: send_msg = input("请输入要发送的消息:").encode("UTF-8") socket_client.send(send_msg) recv_data = socket_client.recv(1024) # 1024是缓冲区大小,一般1024即可 # recv方法是阻塞式,即不接收到返回,就会卡在这一行等待 print("服务端回复消息为:", recv_data.decode("UTF-8")) # 接收的消息需要通过UTF-8解码为字符串
5.关闭连接
socket_client.close() # 最后通过close关闭连接
正则表达式
正则表达式,又称规则表达式,是使用单个字符串来描述、匹配某个句法规则的字符串,常被用来检索、替换那些符合某个模式(规则)的文本。
简单来说,正则表达式就是使用:字符串定义规则,并通过规则去验证字符串是否匹配。
比如:验证一个字符串是否符合条件的电子邮箱地址,只需要配置好正则规则,即可匹配任意邮箱。
Python正则表达式,使用re模块,并基于re模块中三个基础方法来做正则匹配。
分别是:match、search、findall三个基础方法
- re.match(匹配规则,被匹配字符串)
从被匹配字符串开头进行匹配,匹配成功返回匹配对象(包含匹配的信息),匹配不成功返回空。
# 演示Python正则表达式re模块的3个基础匹配方法 import re s = "python hello!" # 如果这里是11python,match就找不到匹配项 # match 从头匹配 result = re.match("python", s) print(result) # 结果:<re.Match object; span=(0, 6), match='python'>
- search(匹配规则, 被匹配字符串)
搜索整个字符串,找出匹配的。从前向后,找到第一个后,就停止,不会继续向后。整个字符串都找不到,返回none
# 演示Python正则表达式re模块的3个基础匹配方法 import re s = "11python hello! python Python" result = re.search("python", s) print(result) # 结果:<re.Match object; span=(2, 8), match='python'>
- findall(匹配规则,被匹配字符串)
匹配整个字符串,找出全部匹配项,找不到返回空list: [ ]
# 演示Python正则表达式re模块的3个基础匹配方法 import re s = "python hello! python Python" # findall 搜索全部匹配 result = re.findall("python", s) print(result) # 结果:['python', 'python']
元字符匹配
单字符匹配:
# 演示Python正则表达式使用元字符进行匹配 import re s = "python nihao 878sdh ah2hfs3 #hdfu" result = re.findall(r'\d', s) # 字符串前面带上r的标记,表示字符串中转义字符无效,就是普通字符的意思 print(result) # 结果:['8', '7', '8', '2', '3']
找出特殊字符: re.findall(r'\W', s)
找出全部英文字母: re.findall(r' [a-zA-Z]', s)
数量匹配
边界匹配
分组匹配
# 正则表达式案例 import re # 匹配账号,只能由字母和数字组成,长度限制6到10位 r = '^[0-9a-zA-Z]{6,10}$' s = '123456_' print(re.findall(r, s)) # 匹配QQ好,要求纯数字,长度5-11,第一位不为0 r = '^[1-9][0-9]{4,10}$' # 是4-10,因为不算第一位,第一位已经单独考虑 s = '12314345' print(re.findall(r, s))
递归
递归是编程中一种非常重要的算法
递归:即方法(函数)自己调用自己的一种特殊编程写法
最典型的递归场景为找出一个文件夹中全部的文件
import os.path def get_file_recursion_from_dir(path): """ 从指定的文件夹中使用递归的方式,获取全部的文件列表 :param path: 被判断的文件夹 :return: list,被包含的全部文件,如果目录不存在或者无文件就返回一个空list """ file_list = [] if os.path.exists(path): # 判断路径是否存在,若存在则将这其中的所有目录的路径补全存放在new_path中 new_path = path + "/" + f if os.path.isdir(new_path): # 判断是否为文件 # 进入到这里,表明这个目录是文件夹而不是文件 file_list += get_file_recursion_from_dir(new_path) # 为文件夹,则递归调动继续记录文件夹内的文件 else: file_list.append(new_path) # 进入这里表明是文件,则放入列表中 else: print(f"指定的目录{path},不存在") return [] # 目录不存在,返回空列表 return file_list if __name__ == '__main__': get_file_recursion_from_dir("D:/test")
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话