Python 测开28期 - 小锅巴 - Python语言高级特性- 学习记录

Python语言高级

Python的多进程/多线程/多协程:多任务编程(concurrent.futures这个库同时提供了:多线程池ThreadPoolExecutor,一个是多进程池:ProcessPoolExecutor。 Python广为使用的并发处理库futures使用入门与内部原理_concurrent库-CSDN博客)

  • 进程与线程的概念:进程是操作系统分配的最小单元,线程时操作系统调度的最小单元;一个应用程序最少包含一个进程(开辟一块内存空间),一个进程可以包含多个线程(多个线程共享这个进程的内存空间,但是:一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存,这就是所说的线程安全问题,共享资源时,需要考虑线程安全)。 一文看懂Python多进程与多线程编程 | 大江狗的博客 (pythondjango.cn)

  • 多进程、多线程、多协程

    • 多线程是最常见的一种多任务编程技术,它可以在同一个程序内同时运行多个线程,每个线程负责执行不同的任务。多线程编程能够充分利用多核心处理器的性能优势,提高程序的并发能力。然而,多线程编程需要注意线程安全问题,比如访问共享资源时需要使用锁来保证数据的一致性。
    • 另一种常见的多任务编程技术是多进程编程,它可以在操作系统级别同时运行多个独立的进程。每个进程拥有独立的内存空间和资源,可以实现更高的隔离性。
    • 另外,协程也是一种轻量级的多任务编程技术,它可以在同一个线程中实现多个任务的切换和调度。协程通过yield语句和生成器函数实现任务的暂停积恢复,避免了线程切换的开销并减少了锁的使用。协程常用于异步编程场景,比如网络编程和I0密集型任务。
      总结起来,多任务编程是一种提高程序并发能力和效率的编程技术,可以通过多线程、多进程或协程等方式实现。在选择多任务编程技术时,需要根据实际需求和情况综合考虑各种因素,比如性能、并发性、开发难度和可维护性等。(一个进程可以有多个线程,一个线程可以有多个协程)
  • 多进程multiprocessing库:

    • Process:用Process()来创建子进程。此方法接收2个参数:target,args,前置传递需要执行的函数名字,后者传函数需要传入的参数
      • 用Process()创建子进程,多进程执行任务示例:
          from multiprocessing import Process
          import time
          import os
          
          def long_time_task():
              print('当前进程id: {}'.format(os.getpid()))
              time.sleep(2)
              print("结果: {}".format(8 ** 20))
          
          
          if __name__ == '__main__':
          
              print(f"当前母进程{os.getpid()}") # 这是母进程
              start = time.time()
              p1 = Process(target=long_time_task)  # 这是子进程1
              p2 = Process(target=long_time_task)  # 这是子进程2
              p1.start()
              p2.start()
              p1.join()  # 让母进程阻塞,等待子进程执行完毕
              p2.join()  # 让母进程阻塞,等待子进程表执行完毕
              end = time.time()
              print("用时{}秒".format((end - start)))
      
    • 用进程池创建进程Pool():
      import os
      import time
      from  multiprocessing import cpu_count,Queue,Manager,Pool, Process
      
      def long_time_task():
          print('当前进程id: {}'.format(os.getpid()))
          time.sleep(2)
          print("结果: {}".format(8 ** 20))
      
      if __name__ == '__main__':
          start = time.time()
          p = Pool(4)  # 创建线程池容器,池子大小:4
          for i in range(3):
              p.apply_async(long_time_task)  # 添加执行的任务
          p.close()  # 调用join()之前必须先调用close()或terminate()方法,让其不再接受新的Process了。
          p.join() # 对Pool对象调用join()方法会等待所有子进程执行完毕;
          end = time.time()
          print("总共用时{}秒".format((end - start)))
      
    • 多进程间的数据共享:进程之间是相互独立的,每个进程都有独立的内存。通过共享内存(nmap模块),进程之间可以共享对象,使多个进程可以访问同一个变量(地址相同,变量名可能不同)。多进程共享资源必然会导致进程间相互竞争,所以应该尽最大可能防止使用共享状态。还有一种方式就是使用队列queue来实现不同进程间的通信或数据共享,这一点和多线程编程类似。
      • 大大
  • 多线程threading库: 一文吃透python多线程(全面总结)_python 获取线程id_Ethan-running的博客-CSDN博客

    • 多线程执行任务
      from threading import Lock, Thread
      
      def long_time_task():
          print('当前进程id: {}'.format(os.getpid()))
          print("当前线程id:{}".format(threading.current_thread().ident))  # 获取当前线程id
          time.sleep(2)
          print("结果: {}".format(8 ** 20))
      
      if __name__ == '__main__':
          start = time.time()
          t1 = Thread(target=long_time_task)  # 创建线程1
          t2 = Thread(target=long_time_task)  # 创建线程2
      
          t1.start()
          t2.start()
          t1.join()
          t2.join()
          end = time.time()
          print("总共用时{}秒".format((end - start)))
      
    • 利用线程池管理线程的创建与销毁:concurrent.futures模块中的ThreadPoolExecutor创建线程池
      python queue 线程池_mob64ca12e2f123的技术博客_51CTO博客
    • 继承Thread类重写的run方法
        class MyTheard(threading.Thread):
        
            def __init__(self):  # 这里可以穿参数进来,比如需要执行的任务的名字,在run中调用执行
                super().__init__()
        
            def run(self):  # 调用start()的时候会自动调用run函数
                print('当前进程id: {}'.format(os.getpid()))
                print("当前线程id:{}".format(threading.current_thread().ident))  # 获取当前线程id
                time.sleep(2)
                print("结果: {}".format(8 ** 20))
        
        
        if __name__ == '__main__':
            start = time.time()
            t1 = MyTheard()
            t2 = MyTheard()
            t1.start()
            t2.start()
            t1.join()
            t2.join()
            end = time.time()
            print("总共用时{}秒".format((end - start)))
      
    • 线程守护
      • Thread类中有deamon的属性,设置了这个属性执行,当主线程结束后,子线程无所谓结束不结束都会跟着主线程结束。(ps:这个属性需要在start()方法之前调用。否则会报错)此属性默认为False
      • join():设置子线程join()后,主线程会阻塞等待子线程完成,再执行join后面的代码
      • threading.enumerate():列表形式返回所有存活的Thread对象
    • 线程锁:Lock、死锁、Rlock
      • 为什么线程有线程锁的概念:
        • 一个进程可以产生多个线程,多个线程可以对共享的数据操作,如果某个线程要更改共享数据时,别的线程也再操作共享数据,会导致共享数据同时被操作导致数据不对。锁的作用:在线程更改共享数据时,先将此线程的操作数据锁住,此时资源处于锁定状态,操作完毕后,将资源解锁,此资源处于非锁定状态,其他的资源才能再次锁定该资源。这就保证了每次只有一个线程对资源进行操作,从而保证了多线程数据情况下数据的正确性。(有点同步的意思)
      • Lock锁:
        def func1():
            global n
            for i in range(1000000):
                # 那些资源共享,就给操作资源的地方上锁
                lock.acquire()  # 上锁
                n += 1
                lock.release()  # 释放锁
        
        def func2():
            global n
            for i in range(1000000):
                lock.acquire()  # 上锁
                n -= 1
                lock.release()  # 释放锁
        
        if __name__ == '__main__':
            start = time.time()
            t1 = Thread(target=func1)  # 创建线程1
            t2 = Thread(target=func2)  # 创建线程2
        
            t1.start()
            t2.start()
            t1.join()
            t2.join()
            end = time.time()
            print("操作n后的值:", n)
            print("总共用时{}秒".format((end - start)))
        
      • 死锁:
      • Rlock锁:为了解决死锁的情况,就有了递归锁:Rlock
    • 线程间的数据共享,线程通信
      • Condition:也可以认为是一把锁,比Lock和Rlock更高级。在其内部维护了一个锁对象,在创建Condigtion对象的时候把锁作为参数传入
        • acquire(): 上线程锁、
        • release(): 释放锁、
        • wait(timeout): 线程挂起,直到收到一个notify通知或者超时(可选的,浮点数,单位是秒s)才会被唤醒继续运行。wait()必须在已获得Lock前提下才能调用,否则会触发RuntimeError
        • notify(n=1): 通知其他线程,那些挂起的线程接到这个通知之后会开始运行,默认是通知一个正等待该condition的线程,最多则唤醒n个等待的线程。notify()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。notify()不会主动释放Lock
        • notifyAll(): 如果wait状态线程比较多,notifyAll的作用就是通知所有线程
        • 示例
               num = 0
               # 生产着
               def product(con: threading.Condition):
                   global num
                   con.acquire()
               
                   print("工厂开始生产")
                   while True:
                       num += 1
                       print(f"已生产数量{num}")
                       time.sleep(1)
                       if num >= 5:
                           print("仓库已满,停止生产")
                           con.notify()
                           con.wait()
                   con.release()
               
               def consumer(con):
                   con.acquire()
                   global num
                   print("开始消费")
                   while True:
                       num -= 1
                       print(f"仓库剩余{num}")
                       time.sleep(2)
                       if num <= 0:
                           print("仓库变空,开始生产")
                           con.notify()
                           con.wait()
                   con.release()
               
               
               if __name__ == '__main__':
                   con = threading.Condition()
               
                   t1 = Thread(target=product, args=(con,))
                   t2 = Thread(target=consumer, args=(con,))
               
                   t1.start()
                   t2.start()
                   t1.join()
                   t2.join()
          
    • 使用queue队列通信,进行数据共享通信
      • 常用的方法:
        • qsize():返回队列的规模
        • empty():如果队列是空的,返回True,不是空的返回Flase
        • full():如果队列满了,返回True,不满Flase
        • get(block,timeout):获取队列,timeout等待时间
        • get_nowait():取出不等待
        • put(item,[,timeout]):放进队列,timeout等待时间,如果队列满了再调这个方法会阻塞线程
        • put_nowait():放入不等待
        • task_done():完成一项工作的时候,task_done()函数像任务已完成的队列发送一个信号
        • join():等队列为空之后,再执行别的操作
      • 用Queue队列实现生产者与消费者:
        def product(con: threading.Condition, que: Queue):
           con.acquire()
           print("工厂开始生产")
           while True:
               que.put(1)
               print(f"生产了:{que.qsize()}")
               time.sleep(1)
               if que.qsize() == 10:
                   con.notify()
                   con.wait()
           con.release()
        
        def consumer(con: threading.Condition, que: Queue):
        
           con.acquire()
           print("开始消费")
           while True:
               que.get()
               time.sleep(2)
               print(f"消费剩余:{que.qsize()}")
               if que.qsize() == 2:
                   con.notify()
                   con.wait()
           con.release()
        
        
        if __name__ == '__main__':
           con = threading.Condition()
           que = queue.Queue()
           t1 = Thread(target=product, args=(con,que))
           t2 = Thread(target=consumer, args=(con,que))
        
           t1.start()
           t2.start()
           t1.join()
           t2.join()
        
        
  • 多协程gevent库:这是一个第三方库,需要pip安装

    • 在3.10版本以后python提供了异步编程的库:asyncio

    • 协程是单线程的,没法利用cpu的多核,想利用cpu的多核可以通过:进程+协程/进程+线程+协程的方式。进程和线程都是通过cpu的调度实现不同任务的有序执行,而协程是由用户程序自己控制调度的,没有线程的切换开销,所以执行效率极高

    • 实现方式:

      • yield关键字实现协程(利用生产器来实现函数的挂起/恢复)
      import time
      
      def producer():
          while True:
              time.sleep(1)
              print("生产了1个包子", time.strftime("%X"))
              yield  # 这里相当于阻塞住了
      
      def consumer():
          while True:
              next(prd)
              print("消费了1个包子", time.strftime("%X"))  # 执行完这里之后回到producer函数里继续执行
      
      if __name__ == '__main__':
          prd = producer()
          consumer()
      
      • greenlet模块(需pip安装):如果有上百个任务之前需要切换,那用yield生成器的方式实现就太鸡肋,而greenlet模块可以轻松的实现很多个任务之前进行切换(需要人显性的去切换任务)

        • 此模块中通过switch()可以切换到指定的协程,可以更简单的进行切换任务
        from greenlet import greenlet
        
        def producer():
            while True:
                time.sleep(1)
                print("生产了1个包子", time.strftime("%X"))
                g2.switch()
        
        def consumer():
            while True:
                print("消费了1个包子", time.strftime("%X"))  # 执行完这里之后回到producer函数里继续执行
                g1.switch()
        
        if __name__ == '__main__':
            g1 = greenlet(producer)
            g2 = greenlet(consumer)
            g1.switch()
        
        
      • gevent模块(需pip安装) 【Python 协程详解】_python 携程-CSDN博客

        • greenlet模块实现了协程并且可以很方便的切换任务但是仍需要人工去切换,而不是自动的进行任务的切换,当一个任务执行时如果遇到IO就会阻塞,没有解决IO阻塞自动切换的问题。gevent模块是对greenlet的再次封装,不用程序员自己变成切换(注意:gevent只有遇到耗时的操作才会切换协程运行,没有耗时的操作不会主动切换)
        • gevent模块识别耗时的两种方式:1、使用gevent模块中重写类:gevent.socket gevent.sleep 2、打补丁的方式,所有的代码from gevent import monkey 导入这个模块,moneky.patch_all()调用这个方法。(后者是给程序打补丁的方式,这样就可以不用改写源码)
        • import gevent
          import time
          from gevent import monkey
          
          monkey.patch_all()
          
          def work1():
              for i in range(5):
                  print("work1开始执行...", gevent.getcurrent())
                  time.sleep(0.5)
          
          def work2():
              for i in range(5):
                  print("work2开始执行...", gevent.getcurrent())
                  time.sleep(0.5)
          
          if __name__ == '__main__':
              g1 = gevent.spawn(work1)
              g2 = gevent.spawn(work2)
              g1.join()
              g2.join()
          
        • 当开启的协程很多的时候,一个个的调用join方法就有点麻烦,所以gevent提供了一个方法joinall(),可以一次join所有的协程。joinall() 方法传参一个列表,列表包含了所有的协程。gevent.joinall([gevent.spawn(producer, “zhangsan”), gevent.spawn(consumer, “lisi”)])
      • 高版本的python内置了:asyncio库来实现异步协程。由于是内置的会比第三方库来实现性能更优

        • 此库引入新的语法:async和await
          • 其中async关键字用来声明一个函数为异步函数,异步函数的特点是能在函数执行过程中挂起,去执行其他的异步函数,等到挂起条件消失后再回来继续执行。
          • await关键字用来实现异步挂起,。比如某一异步任务执行到某一步时需要较长时间的耗时操作,就将此挂起,去执行其他的异步程序。(注意:await后面只能跟异步程序或者__await__属性的对象)
          • 示例:
                 import asyncio
                  async def work1():  # 声明异步函数
                      for i in range(5):
                          print("work1开始执行...", gevent.getcurrent())
                          await asyncio.sleep(0.5)  # 耗时操作,挂起
                  
                  
                  async def work2():
                      for i in range(5):
                          print("work2开始执行...", gevent.getcurrent())
                          await asyncio.sleep(0.5)  # 耗时操作,挂起
                  
                  if __name__ == '__main__':
                      loop = asyncio.get_event_loop() # 创建事件循环
                      task = [asyncio.ensure_future(work1()), asyncio.ensure_future(work2())]  # 创建任务列表
                      loop.run_until_complete(asyncio.wait(task))  # 将你任务注册到事件中
                      loop.close()

Python的网络编程 【精选】Python – 网络编程_python网络编程-CSDN博客

  • ip地址:用来找唯一主机的,用来指引数据包的收发方向
  • 端口:端口只有整数,范围从0-65535.动态端口是:1024-65535.
    • 端口的作用:IP地址+端口来区分不同的进程服务。
  • socket概念:
    • 所谓进程指的是:运行的程序以及运行时用到的资源这个整体称之为进程
    • 所谓进程间通信指的是:运行的程序之间的数据共享
    • 什么是socket:
      • socket(简称:套接字):是进程间的一种通信方式。它与其他进程间通信的一个不同是:它能实现不同主机间的进程间通信,网络上各种各样的服务大多都是基于socket来完成通信的。
    • Python中的socket模块的函数socket
      • 创建socket:socket.socket(AddressFamily, Type):AddressFamily–可以选择AF_INET(用于Internet进程间通信)、AF_UNIX(用于同一台机器进程间通信);Type:套接字类型,可以是:SOCK_STREAM(流式套接字,主要用于TCP协议)、SOCK_DGRAM(数据报套接字,主要用于UDP协议)
      • s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 这是创建了一个:Internet进程间通信,tcp协议
        s_u = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 这是创建了一个:Internet进程间通信,udp协议
        s.close()  # 关闭套接字
        
      • 套接字的使用流程和文件的使用流程类似:
        • 1、创建套接字 2、使用套接字收/发数据 3、关闭套接字
      • UDP:
        import socket
        
        # s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 这是创建了一个:Internet进程间通信,tcp协议
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 这是创建了一个:Internet进程间通信,udp协议
        data = input("请输入内容:")  # 输入内容
        addr = ("127.0.0.1", 8011)  # 发送数据的IP地址,与端口
        s.sendto(data.encode("gbk"), addr) # 发送
        recv_data = s.recvfrom(1024)  # 接收发送的数据
        print(recv_data) 
        print(recv_data[0].decode("gbk"))  # 第一个值数数据
        print(recv_data[1])  # 第二个值是端口号
        
        s.close()  # 关闭套接字
        
      • TCP:
        • TCP通信需要经过:创建连接、数据传送、终止连接。TCP通信之前需要先建立相关的链接。
        • TCP通信模型:
        • TCP完成一个客户端的功能
          import socket
          # 模拟客户端
          
          s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 创建TCP套接字
          IP = "127.0.0.1"
          PORT = 8080
          s.connect((IP, PORT))  # 与服务端创建连接
          send_data = input("请输入发送的数据:")
          s.send(send_data.encode('gbk'))  # 发送数据
          rescData = s.recv(1024)  # 接收数据
          print(rescData.decode('gbk'))
          s.close()
          
        • TCP完成一个服务器的功能
          -1、创建套接字 2、bind绑定IP和port 3、listen使套接字变为被动链接 4、accept等待客户端的链接 5、recv/send接收发数据
          • Demo示例:
          import socket
          # 模拟服务端
          
          tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建TCP套接字
          IP = ""
          PORT = 8080
          tcp_server_socket.bind((IP, PORT))  # 服务端绑定地址信息
          tcp_server_socket.listen(128)  # 将主动套接字变成被动套接字
          
          # 如果有客户端来链接服务器,那么就产生一个新的套接字专门为这个客户端服务
          # client_socket就是用来为这个客户端服务的
          # tcp_server_socket就可以省下来专门等待其他的新客户端链接
          client_socket, clientAddr = tcp_server_socket.accept()
          print("已与客户端建立连接")
          
          data = client_socket.recv(1024)
          print("接收到的数据:",data.decode('gbk'))
          send_data = input("请输入返回数据:")
          send = client_socket.send(send_data.encode('gbk'))
          
          # 关闭为这个客户端服务的套接字,只要关闭了,就意味着为不能再为这个客户端服务了,如果还需要服务,只能再次重新连接
          client_socket.close()
          
        • TCP连接的注意事项:
          • tcp一般情况下都是需要绑定ip/port的,否则客户端找不到这个服务器
          • tcp客户端一般不绑定,主动连接服务器不绑定。只要确定好服务器的ip/port,客户端随时可以链接
          • tcp服务器通过listen可以将socket创建出来的主动套接字变成被动的,这是tcp作为服务器必须要做的
          • 客户端连接服务器时,就需要使用connect连接,udp不需要连接直接发送就行。
          • 当tcp客户端连接服务器时,服务器会产生1个先的套接字,这个新的套接字标记这个客户端后,单独为这个客户端服务
          • listen后的套接字是被动套接字,用来接收新的客户端链接请求的,而accept返回的是新套接字标记的这个客户端
          • 关闭listen后的套接字意味着被动套接字关闭,会导致新的客户端不能链接服务器,但是之前已经链接成功的客户端正常通信
          • 关闭accept返回的套接字意味着这个客户端已经服务完毕
          • 客户端的套接字调用close后,服务器的recv堵塞,并返回的长度是0,因为服务器可以根据返回数据的长度来区别客户端是否已经下线

Python进行数据库操作

  • 常用的的库:pymysql、dbutils(下的PooledDB可以进行连接池设置)、sshtunnel(需要通过ssh通道连接,需要借助这个库)
  • 不需要ssh连接的数据库操作
    • Demo示例
    class MysqlClient(object):
        """
        Mysql客户端
        """
    
        def __init__(self, host=None, port=3306, user=None, password=None, db=None, charset='utf8'):
            """
            数据库工具类初始化方法
            Args:
                host: 数据库地址
                port: 数据库端口
                user: 数据库用户名
                password: 数据库密码
                db: 数据库名
                charset: 数据库编码
            """
            self.host = host
            self.port = port
            self.user = user
            self.password = password
            self.db = db
            self.charset = charset
            # self.db_pool = PooledDB(pymysql, mincached=1, maxcached=5, host=self.host, port=self.port,
            #                         user=self.user, passwd=self.password, db=self.db, charset=self.charset,
            #                         connect_timeout=60)
            self.c = pymysql.connect(host=self.host, port=self.port,
                                    user=self.user, passwd=self.password, db=self.db, charset=self.charset)
    
    
  • 需要ssh连接的数据库操作
    • Demo示例
      class MysqlClient(object):
          """
          Mysql客户端
          """
      
          def __init__(self, host=None, port=3306, user=None, password=None, db=None, charset='utf8', ssh_host=None,
                       ssh_username=None, ssh_password=None, use_ssh_tunnel=False):
              """
              数据库工具类初始化方法
              Args:
                  host: 数据库地址
                  port: 数据库端口
                  user: 数据库用户名
                  password: 数据库密码
                  db: 数据库名
                  charset: 数据库编码
                  ssh_host: ssh地址
                  ssh_username: ssh用户名
                  ssh_password: ssh密码
                  use_ssh_tunnel: 使用ssh隧道
              """
              self.host = host
              self.port = port
              self.user = user
              self.password = password
              self.db = db
              self.charset = charset
              self.ssh_host = ssh_host
              self.ssh_username = ssh_username
              self.ssh_password = ssh_password
              self.use_ssh_tunnel = use_ssh_tunnel
              self.tunnel = None
              try:
                  if self.use_ssh_tunnel:
                      self.tunnel = SSHTunnelForwarder(
                          ssh_address_or_host=(self.ssh_host, 22),
                          ssh_username=self.ssh_username,
                          ssh_password=self.ssh_password,
                          remote_bind_address=(self.host, self.port))
                      self.tunnel.start()
      
                      self.db_pool = PooledDB(pymysql, mincached=1, maxcached=5, host='127.0.0.1',
                                              port=self.tunnel.local_bind_port, user=self.user, passwd=self.password,
                                              db=self.db, charset=self.charset)
                  else:
                      self.db_pool = PooledDB(pymysql, mincached=1, maxcached=5, host=self.host, port=self.port,
                                              user=self.user, passwd=self.password, db=self.db, charset=self.charset,
                                              connect_timeout=60)
              except Exception as e:
                  if self.tunnel is not None:
                      self.tunnel.close()
                  logger.error('Mysql数据库工具初始化异常,原因:{}'.format(str(e)))
      
      

Python的高级用法

  • 3.10版本增加类似Java_switch case语句 结构化模式匹配:match() case(): 【Python】Python 3.10 新特性之 match case语句_python match case-CSDN博客
    match (s):  # 可以是单个,也可以是多个
        case "a": # 配置条件
            print("s:", s)
        case "b":
            print("s", s)
        case _:
            print("默认值")
    
  • Python内置魔术方法: Python魔术方法详解_彭阳的技术博客_51CTO博客(魔术方法不需要主动调用,存在的目的是给Python的解释器进行调用的,几乎每一个魔术方法都有一个对应的内置函数)
    • __init__():初始化-用于初始化对象的属性和状态。
    • __del__():当对象不在被使用时执行清理操作,此魔术方法用的不多,Python有自己的垃圾回收机制。
    • __hash__()内建函数hash():返回一个整数值,实现魔术方法的类可被hash。
    • __eq__():与==对象,判断两个对象是否相等
    • __bool__()内建函数bool():布尔
    • __repr__()内建函数repr():对一个对象获取字符串表达式,如果没有定义__str__也会调用此方法
    • __str__()内建函数str():返回对象的字符串表达式,没有定义调__repr__,也没有定义的话防护object的内存地址信息
    • 运算符魔术方法(不常用):见学习连接
    • __len__():查看对象的长度
    • __iter__():迭代容器时,调用,返回一个新的迭代去对象
    • __contains__():in成员运算符,如果没有实现这个方法,就会调用__iter__方法遍历
    • __getitem__():实现self[key]的访问,序列化对象,key接受整数作为索引。
    • __setitem__():和__getitem__类似是设置值的方法
    • __missing__():字典调用_getitem__时,key不存在时执行该方法
    • __call__():类中实现这个方法,实例就可以像调用函数一样去调用实例
    • 上线文管理对象:当一个对象同时实现了这面的两个方法,就变成了上下文管理器
      • __enter__():进入与对象相关的上下文,如果存在此方法,with语法会把该方法返回的值,绑定到as 后面的变量身上
      • __exit__(exc_type, exc_value, exc_tb):退出与此对象相关的上下文。参数都和异常有关,异常的类型,异常的值,异常的追踪信息
    • 反射:概述–Python中,能通过一个对象找出其type、class、attribute、或method的能力,称为反射或者自省。
      • 具有反射能力的函数:type()、isinstance()、callable()、dir()、getattr()
      • getattr(object,name[default]):通过name返回object对象的属性值,当属性不存在时,将使用default返回,如果没有default,则抛出AttributeError的错误
      • setattr(object, name, value):object的属性存在,则覆盖,不存在,新增
      • hasattr(object, name):判断对象是否有name名字的属性,name必须为字符串
    • 反射相关的魔术方法:
      • __getattr__(): 一个类的属性会按着继承关系去找,如果找不到会执行__getattr__()这个方法的实现,如果没有定义这个方法,就会抛出AttributeError的错误;
      • __setattr__():可以拦截对实例属性的增加,修改操作,如果要设置生效,需要自己操作实例的__dict__()方法;
      • __delattr__(): 可以阻止通过实例删除属性的操作,,但是通过类依然可以删除属性;
      • __getattribute__():实例的所有属性访问都会调用这个方法,它阻止的属性的查找,该方法返回值或者抛出AttributeError异常;如果抛出了AttributeError这个异常,会紧跟着调用__getattr__方法,因为表示属性没有存在
    • 描述器:一个类如果实现了__get__/set/__delete__三个方法任意一个方法,就是描述器。如果只实现__get__这一个方法,就是非数据描述符,如果同时也实现了__set__方法,就是数据描述符。一个类的类属性设置为描述器,那么被称为owner属性。
      • 属性的访问顺序:__dict__优先于非数据描述符,数据描述符优先于__dict__