加入收藏 | 设为首页 | 会员中心 | 我要投稿 台州站长网 (https://www.0576zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 编程要点 > 语言 > 正文

Python 中类的构造方式 __New__ 的妙用

发布时间:2021-11-04 10:00:57 所属栏目:语言 来源:互联网
导读:Python 的类中,所有以双下划线__包起来的方法,叫魔术方法,魔术方法在类或对象的某些事件发出后可以自动执行,让类具有神奇的魔力,比如常见的构造方法__new__、初始化方法__init__、析构方法__del__,今天来聊一聊__new__的妙用,主要分享以下几点: __ne
Python 的类中,所有以双下划线__包起来的方法,叫魔术方法,魔术方法在类或对象的某些事件发出后可以自动执行,让类具有神奇的魔力,比如常见的构造方法__new__、初始化方法__init__、析构方法__del__,今天来聊一聊__new__的妙用,主要分享以下几点:   __new__ 和 __init__ 的区别 应用1:改变内置的不可变类型 应用2:实现一个单例 应用3:客户端缓存 应用4:不同文件不同的解密方法 应用5:Metaclasses __new__ 和 __init__ 的区别 1、调用时机不同:new 是真正创建实例的方法,init 用于实例的初始化,new 先于 init 运行。   2、返回值不同,new 返回一个类的实例,而 init 不返回任何信息。   3、new 是 class 的方法,而 init 是对象的方法。   示例代码:   class A:      def __new__(cls, *args, **kwargs):          print("new", cls, args, kwargs)          return super().__new__(cls)        def __init__(self, *args, **kwargs):          print("init", self, args, kwargs)      def how_object_construction_works():      x = A(1, 2, 3, x=4)      print(x)          print("===================")      x = A.__new__(A, 1, 2, 3, x=4)      if isinstance(x, A):          type(x).__init__(x, 1, 2, 3, x=4)      print(x)    if __name__ == "__main__":      how_object_construction_works()  上述代码定义了一个类 A,在调用 A(1, 2, 3, x=4) 时先执行 new,再执行 init,等价于:   x = A.__new__(A, 1, 2, 3, x=4)  if isinstance(x, A):      type(x).__init__(x, 1, 2, 3, x=4)  代码的运行结果如下:   new <class '__main__.A'> (1, 2, 3) {'x': 4}  init <__main__.A object at 0x7fccaec97610> (1, 2, 3) {'x': 4}  <__main__.A object at 0x7fccaec97610>  ===================  new <class '__main__.A'> (1, 2, 3) {'x': 4}  init <__main__.A object at 0x7fccaec97310> (1, 2, 3) {'x': 4}  <__main__.A object at 0x7fccaec97310>  new 的主要作用就是让程序员可以自定义类的创建行为,以下是其主要应用场景:   应用1:改变内置的不可变类型 我们知道,元组是不可变类型,但是我们继承 tuple ,然后可以在 new 中,对其元组的元素进行修改,因为 new 返回之前,元组还不是元组,这在 init 函数中是无法实现的。比如说,实现一个大写的元组,代码如下:   class UppercaseTuple(tuple):      def __new__(cls, iterable):          upper_iterable = (s.upper() for s in iterable)          return super().__new__(cls, upper_iterable)        # 以下代码会报错,初始化时是无法修改的      # def __init__(self, iterable):      #     print(f'init {iterable}')      #     for i, arg in enumerate(iterable):      #         self[i] = arg.upper()    if __name__ == '__main__':      print("UPPERCASE TUPLE EXAMPLE")      print(UppercaseTuple(["hello", "world"]))    # UPPERCASE TUPLE EXAMPLE  # ('HELLO', 'WORLD')  应用2:实现一个单例 class Singleton:      _instance = None        def __new__(cls, *args, **kwargs):          if cls._instance is None:              cls._instance = super().__new__(cls, *args, **kwargs)          return cls._instance      if __name__ == "__main__":        print("SINGLETON EXAMPLE")      x = Singleton()      y = Singleton()      print(f"{x is y=}")  # SINGLETON EXAMPLE  # x is y=True  应用3:客户端缓存 当客户端的创建成本比较高时,比如读取文件或者数据库,可以采用以下方法,同一个客户端属于同一个实例,节省创建对象的成本,这本质就是多例模式。   class Client:      _loaded = {}      _db_file = "file.db"        def __new__(cls, client_id):          if (client := cls._loaded.get(client_id)) is not None:              print(f"returning existing client {client_id} from cache")              return client          client = super().__new__(cls)          cls._loaded[client_id] = client          client._init_from_file(client_id, cls._db_file)          return client        def _init_from_file(self, client_id, file):          # lookup client in file and read properties          print(f"reading client {client_id} data from file, db, etc.")          name = ...          email = ...          self.name = name          self.email = email          self.id = client_id      if __name__ == '__main__':      print("CLIENT CACHE EXAMPLE")      x = Client(0)      y = Client(0)      print(f"{x is y=}")      z = Client(1)  # CLIENT CACHE EXAMPLE  # reading client 0 data from file, db, etc.  # returning existing client 0 from cache  # x is y=True  # reading client 1 data from file, db, etc. 

(编辑:台州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读