2023年7月30日星期日

py 技巧

py 技巧

python部分其他功能

参考视频

闭包

定义双层嵌套函数,内层函数可以访问外层函数的变量
将内存函数作为外层函数的返回,此内层函数就是闭包函数
优点,使用闭包可以让我们得到:
无需定义全局变量即可实现通过函数,持续的访问、修改某个值
闭包使用的变量的所用于在函数内,难以被错误的调用修改
缺点:
由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存

在闭包函数(内部函数中)想要修改外部函数的变量值
需要用nonlocal声明这个外部变量

# 让内部的函数依赖外部的变量
def outer(logo):
    def inner(msg, is_modify=False):
        # 如果要在闭包内部函数中修改外部函数的变量,只需要用Nonlocal关键字
        if is_modify is True:
            nonlocal logo
            logo = "kirisame"
        print(f"fixed{logo}:flexible{msg}")

    return inner  # 返回一个函数,并且这个函数可以接受一个参数,功能是打印一个字符串


fn1 = outer("touhou")
fn1("yoyomu")

outer("hakurei")("reimu")

fn1("marisa", True)

装饰器

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

def sleep():
    import random
    import time

    print("Sleeping")
    time.sleep(random.randint(1, 5))

# 现在希望给此函数增加功能,调用sleep前后各输出一句话
# 1.闭包写法
def outer(func):
    def inner():
        print("Advanced")
        func()
        print("awake")

    return inner

exsleep = outer(sleep)
exsleep()

# 2.快捷写法,语法糖
"""
装饰器就是使用创建一个闭包函数,在闭包函数内调用目标函数
可以达到不改动目标函数的同时,增加额外的功能
"""

@outer
def study():
    print("studying")

study()  # 本质上调用了Inner()

设计模式

设计模式是一种编程套路,可以极大的方便程序的开发
最常见、最经典的设计模式,就是我们所学习的面向对象了

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

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

单例模式

单例模式就是对一个类,只获取其唯一的类实例对象,持续复用它

  • 节省内存
  • 节省创建对象的开销

工厂模式

当需要大量创建一个类的实例的时候,可以使用工厂模式。
即,从原生的使用类的构造去创建对象的形式迁移到,基于工厂提供的方法去创建对象的形式

class Person:
    pass
class Worker(Person):
    pass
class Student(Person):
    pass
class Teacher(Person):
    pass
worker = Worker()
stu = Student()
teacher = Teacher()

class Person:
pass
class Worker(Person):
pass
class Student(Person):
pass
class Teacher(Person):
pass
class Factory:
    def get_person(self,p_type):
        if p_type == 'w':
            return Worker()
        elif p_type =='s':
            return Student0
        else:
            return Teacher()
factory = Factory()
worker = factory.get_person('w')
stu = factory.get_person('s')
teacher = factory.get_person('t')

使用工厂类的get_person()方法去创建具体的类对象
将对象的创建由使用原生类本身创建
转换到由特定的工厂方法来创建
优点:

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

py多线程

现代操作系统比如MacOSX,UNIX,Linux,Windows等,都是支持“多任务”的操作系统
进程:就是一个程序,运行在系统之上,那么便称之这个程序为一个运行进程,并分配进程ID方便系统管理
线程:线程是归属于进程的,一个进程可以开启多个线程,执行不同的工作,是进程的实际工作最小单位
进程就好比一家公司,是操作系统对程序进行运行管理的单位
线程就好比公司的员工,进程可以有多个线程(员工)是进程实际的工作者

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

进程之间是内存隔离的,
即不同的进程拥有各自的内存空间。这就类似于不同的公司拥有不同的办公场所。
线程之间是内存共享的,线程是属于进程的,一个进程内的多个线程之间是共享这个进程所拥有的内存空间的。这就好比,公司员工之间是共享公司的办公场所。

并行执行

并行执行的意思指的是同一时间做不同的工作
进程之间就是并行执行的,操作系统可以同时运行好多程序,这些程序都是在并行执行
除了进程外线程其实也是可以并行执行的。
也就是比如一个Python程序, 其实是完全可以做到

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

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

Threading模块

import threading
thread_obj = threading.Thread([group [,target [, name [, args[,kwargs]]]]])
# group: 暂时无用,未来功能的预留参数
# target: 执行的目标任务名
# args: 以元组的方式给执行任务传参
#kwargs: 以字典方式给执行任务传参
# name: 线程名,一般不用设置

# 启动线程,让线程开始工作
thread_obj.start()
# 简单实例
import threading
import time
def sing():
    while True:
        print("1")
        time.sleep(1)
def dance():
    while True:
        print("2")
        time.sleep(1)
sing_thread = threading.Thread(target=sing)
dance_thread = threading.Thread(target=dance)

sing_thread.start()
dance_thread.start()

需要传参的话可以通过

  • args参数通过元组(按参数顺序)的方式传参
  • 或使用kwargs参数用字典的形式传参
def dance(msg:str):
    while True:
        print(msg)
        time.sleep(1)
dance_thread = threading.Thread(target=dance,args=("x",))
dance_thread.start()

def dance(m1:str,m2:str):
    while True:
        print(m1,m2)
        time.sleep(1)
dance_thread = threading.Thread(target=dance,kwargs={"m2":"456","m1":"123"})
dance_thread.start()

更多关于多线程

socket通信

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

2个进程之间通过Socket进行相互通讯,就必须有服务端和客户端
Socket服务端:等待其它进程的连接、接受发来的消息、可以回复消息
Socket客户端:主动连接服务端、可以发送消息、可以接收回复

host

  1. 创建socket对象
import socket
socket_server = socket.socket()
  1. 绑定socket server到指定IP和地址
socket_server.bind((host:str, port:int)) # 传入一个有两个元素的元组
  1. 服务端开始监听端口
socker_server.listen(backlog)
# backlog为int整数,表示允许的连接数量,超出的会等待,可以不填,不填会自动设置一个合理值
  1. 接收客户端连接,获得连接对象
conn, address = socket_server.accept()
print(f"接收到客户端连接,连接来自: {address}")
# accept方法是阻塞方法,如果没有连接,会卡再当前这一行不向下执行代码
# accept返回的是一个二元元组,可以使用上述形式,用两个变量接收二元元组的2个元素
  1. 客户端连接后,通过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,来退出无限循环
  1. 通过conn(客户端当次连接对象)调用send方法可以回复消息
while True:
    data = conn.recv(1024).decode("UTF-8")
    if data == 'exit':
    break
print("接收到发送来的数据:",data)
conn.send("你好呀哈哈哈”).encode("UTF-8)
  1. conn(客户端当次连接对象)和socket server对象调用close方法,关闭连接

0 评论:

发表评论