Python高级技巧

十三、Python高级技巧

1. 闭包

解决全局变量问题:

  • 代码在命名空间上(变量定义)不够干净、整洁
  • 全局变量又被修改的风险
  • 定义:

    ​ 在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。

  • 简单闭包

    def outer(logo: str):
        def inner(msg: str):
            print(f"<{logo}><{msg}><{logo}>")
    
        return inner
    
    
    fn1 = outer('稀土掘金') 
    fn1('xzq')  # <稀土掘金><xzq><稀土掘金>
    fn1('XZQ0723')  # <稀土掘金><XZQ0723><稀土掘金>
    
  • nonlocal关键字

    在闭包函数(内部函数中)想要修改外部函数的变量值,需要用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
    
  • 优点:

    • 无需定义全局变量即可实现通过函数,持续的访问、修改某个值
    • 闭包使用的变量的所用于在函数内,难以被错误的调用修改
  • 缺点:

    • 由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存

2. 装饰器

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

  • 装饰器的一般写法

    • 希望给sleep函数,增加一个功能
      • 在调用sleep前输出:我要睡觉了
      • 在调用sleep后输出:我起床了
    # 装饰器的一般写法(闭包)
    def sleep():
        import random
        import time
        print('睡眠中...')
        time.sleep(random.randint(1, 5))
    
    
    def outer(func):
        def inner():
            print('我睡了')
            func()
            print('我醒了')
    
        return inner
    
    
    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()
      

3. 多线程

  • 进程和线程

    ​ 进程:是操作系统中正在运行的程序的实例。每个进程都有自己的内存空间、代码、数据等等。进程之间是独立的,它们无法互相访问对方的内存空间,只有通过进程间通信才能实现数据的共享。

    ​ 线程:相对于进程而言,线程的开销更小,调度更快捷。另外,线程的并发能力更强,对于多核CPU来说也更加高效,因为不同的线程可以在不同的CPU内核上同时执行。但是,线程之间的共享变量可能会导致线程安全问题,需要加锁保护。

    在这里插入图片描述

  • 多线程网络编程

    Python的多线程可以通过threading模块来实现。

    • 语法

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

    import threading
    import time
    
    
    def sing(msg):
        while True:
            print(msg)
            time.sleep(1)
    
    
    def dance(msg):
        while True:
            print(msg)
            time.sleep(1)
    
    
    if __name__ == '__main__':
        t1 = threading.Thread(target=sing, args=('啦啦啦',))
        t2 = threading.Thread(target=dance, kwargs={"msg": "舞舞舞"})
        t1.start()
        t2.start()
    

4. Socket网络编程

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

  • 2个进程之间通过Socket进行相互通讯,就必须有服务端和客户端

    • Socket服务端:等待其它进程的连接、可接受发来的消息、可以回复消息

    • Socket客户端:主动连接服务端、可以发送消息、可以接收回复

  • 服务端示例

    import socket
    
    # 1.创建Socket对象
    socket_server = socket.socket()
    
    # 2.绑定ip地址和端口 服务端bind
    socket_server.bind(("localhost", 8888))
    
    # 3.监听端口
    # listen方法内的参数表示可以链接的次数
    socket_server.listen(1)
    
    # 4.等待客户端连接
    # result为一个元组 ,accept()方法是阻塞的方法,等待客户端的连接如果没有连接,卡在这一行,就不会往下执行
    # result = socket_server.accept()
    # conn = result[0]  # 客户端的连接对象
    # address = result[1]  # 客户端的地址信息
    conn, address = socket_server.accept()
    
    print(f'接收到客户端的链接,客户端的信息是:{address}')
    while True:
        # 5.接收客户端的信息,要使用客户端和服务端的本次连接对象,而非socket_server对象
        # recv的接收的参数事缓冲区大小,一般是1024即可,
        # 返回值是一个字节数组,也就是bytes对象,不是字符串,可以通过decode方法通过UTF-8编码,将字节数组转换为字符串对象
        #
        data: str = conn.recv(1024).decode('UTF-8')
        print(f'客户端发来的消息是:{data}')
    
        # 6. 发送回复信息
        # encode可以将字符串编码为字节数组
        msg = input('请输入你要和客户端回复的消息:')
        if msg == 'exit':
            break
        conn.send(msg.encode('UTF-8'))
    
    # 7.关闭连接
    conn.close()
    socket_server.close()
    
  • 客户端示例

    import socket
    
    # 1.创建Socket对象
    socket_client = socket.socket()
    
    # 2.连接到服务端ip地址和端口 客户端connect
    socket_client.connect(("localhost", 8888))
    while True:
        msg = input('请输入要给服务端发送的信息:')
        if msg == 'exit':
            break
        # 3.发送消息
        socket_client.send(msg.encode("UTF-8"))
    
        # 4.接收返回消息
        recv_data = socket_client.recv(1024)
        print(f'服务端返回的消息是: {recv_data.decode("UTF-8")}')
    
    # 5.关闭连接
    socket_client.close()
    

5. 正则表达式

​ 正则表达式,又称规则表达式(Regular Expression),是使用单个字符串来描述、匹配某个句法规则的字符串,常被用来检索、替换那些符合某个模式(规则)的文本。简单来说,正则表达式就是使用:字符串定义规则,并通过规则去验证字符串是否匹配。

  • 基本匹配

    import re
    
    s = 'xzq python xzq python xzq python'
    # match 从头匹配
    result1 = re.match('xzq', s)
    print(F'match 结果:{result1}')
    # print(result1.span())
    # print(result1.group())
    
    # search 搜索匹配 从前向后,找到第一个就停止
    result2 = re.search('python', s)
    print(F'search 结果:{result2}')
    # print(result2.span())
    # print(result2.group())
    
    # findall 搜索全部匹配
    result3 = re.findall('python', s)
    print(F'search 结果:{result3}')
    
  • 元字符匹配

    • 单字符匹配

      字符功能
      .匹配任意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匹配非单词的边界
    • 分组匹配

      字符功能
      |匹配左右任意一个表达式
      ()将括号中字符作为一个分组

    示例:

    import re
    
    s = "!!xzq %^$&*xzq 22311 xzq_666 @@ ll"
    result = re.findall(r'[a-zA-Z0-9]', s)  # 字符串前带r,表示字符串中转义字符无效
    print(result)
    # 匹配账号,只能由字母和数字组成,长度限制6到10位
    result1 = re.findall(r'^[a-zA-Z0-9]{6,10}$', '4556xzq')
    print(result1)
    # 匹配QQ号,要求纯数字,长度5-11,第一位不为0
    result2 = re.findall(r'^[1-9][0-9]{4,10}$', '710675281')
    print(result2)
    
    # 匹配邮箱地址,只允许qq、163、gmail这三种邮箱地址
    result3 = re.match(r'(^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+$)', '710.675281@qq.com')
    print(result3)
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嘎嘎油

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值