美文网首页
Python元类及单例设计模式案例解析

Python元类及单例设计模式案例解析

作者: 白码会说 | 来源:发表于2020-11-13 22:04 被阅读0次

    Time will tell.

    一、元类

    元类用于创建类的类。在 python 中,万物皆对象,类当然也是对象。

    对象是通过类实例化产生的,如果类也是对象的话,必然类对象也是由另一个类实例化产生的。

    class Person:
        pass
    
    p = Person()
    print(type(p), type(Person), type(object))
    # <class '__main__.Person'> <class 'type'> <class 'type'>
    # Person类是通过type类实例化产生的(object类内部是由C语言实现的)
    
    # 直接调用type类来产生类对象(默认情况下所有类的元类都是type)
    class Student:
        pass
    
    print(type(Student))
    # <class 'type'>
    
    

    二、类的基本组成

    1. 类的名字(字符类型)
    2. 类的父类们 (是一个元组或列表)
    3. 类的名称空间(字典)

    三、元类的目的

    可以高度地自定义类,例如控制一个类的名字必须以大驼峰的方式来书写。

    类也是对象,也有自己的类。

    创建类对象做一些限制

    • 想到了初始化方法,我们只要找到了类对象的类(或者说元类),覆盖其中 init方法就能实现需求。
    • 当然我们不能修改源代码,所以应该继承type来编写自己的元类,同时覆盖 init来完成需求。
    '''
    只要继承了type, 那么这个类就变成了一个元类
    
    '''
    
    class MyType(type):  # 定义一个元类
        def __init__(self, cls_name, bases, dict):
            print(cls_name, bases, dict)
            # Pig () {'__module__': '__main__', '__qualname__': 'Pig'}
            if not cls_name.istitle():
                raise Exception("好好写类名!")
            super().__init__(cls_name, bases, dict)
    
        pass
    
    # class pig(metaclass=MyType):  # 为Pig类指定了元类为MyType
        # 直接报错,Exception: 好好写类名!
    class Pig(metaclass=MyType):
    
        pass
    
    # # MyType("pig", (), {})  # Exception: 好好写类名!
    print(MyType("Pig", (), {}))
    # <class '__main__.Pig'>
    
    

    元类中的 call 方法

    • 当你调用类对象时,会自动执行元类中的 call方法,并将这个类作为第一个参数传入,以及后面的一堆参数。

    • 覆盖元类中的call之后,这个类就无法产生对象对象无法实例化,必须调用super().call()来完成对象的创建并返回其返回值。

    call与 init 的使用场景

    • 想要控制对象的创建过程用 call
    • 想要控制类的创建过程用 init
    '''
    需求:想要把对象的所有属性变成大写
    '''
    
    class MyType(type):
        def __call__(self, *args, **kwargs):
            new_args = [item.upper() for item in args]
            return super().__call__(*new_args, **kwargs)
    
    class Person(metaclass=MyType):  # ---> Person = MyType("Person", (), {})
        def __init__(self, name):
            self.name = name
    
    p = Person('jack')
    print(p.name)
    # JACK
    
    
    # 要求创建对象时必须以关键字传参
    #   覆盖元类的__call__
    #   判断有没有传非关键字参数,有就不行
    
    class MyMeta(type):
        # def __call__(self, *args, **kwargs):
        #     obj = object.__new__(self)
        #     self.__init__(obj, *args, **kwargs)
        #     return obj
    
        def __call__(self, *args, **kwargs):
            if args:
                raise Exception("非法传参(只能以关键字的形式传参!)")
            return super().__call__(*args, **kwargs)
    
    class MyClass(metaclass=MyMeta):
        def __init__(self, name):
            self.name = name
    
    # my_class_obj = MyClass('123')
    # # 报错Exception: 非法传参(只能以关键字的形式传参!)
    my_class_obj = MyClass(name='123')
    print(my_class_obj)
    # <__main__.MyClass object at 0x000002161DD187B8>
    
    

    一旦覆盖了call必须调用父类的call方法来产生对象并返回这个对象。

    补充 new方法

    当你要创建类对象时( 类 + () ),会首先执行元类中的 new方法,拿到一个空对象,然后会自动调用 init方法来对这个类进行初始化操作。

    class MyMeta(type):
        # def __call__(self, *args, **kwargs):
        #     obj = object.__new__(self)
        #     self.__init__(obj, *args, **kwargs)
        #     return obj
        pass
    
    

    如果你覆盖了 new方法,则必须保证 new方法必须有返回值,且必须是对应的类对象。

    class Meta(type):
    
        def __new__(cls, *args, **kwargs):
            print(cls)  # 元类自己
            print(args)  # 创建类需要的几个参数  类名,基类,名称空间
            print(kwargs)  # 空的
            print("__new__ run")
            # return super().__new__(cls,*args,**kwargs)  # 等同于下面这句
            obj = type.__new__(cls, *args, **kwargs)
            return obj
    
        def __init__(self, a, b, c):
            super().__init__(a, b, c)
            print("__init__ run")
    
    class A(metaclass=Meta):
        pass
    
    print(A)
    # <class '__main__.Meta'>
    # ('A', (), {'__module__': '__main__', '__qualname__': 'A'})
    # {}
    # __new__ run
    # __init__ run
    # <class '__main__.A'>
    
    

    newinit都可以实现控制类的创建过程,但 init更简单。

    四、最后来说说单例设计模式

    1、设计模式用于解决某种固定问题的套路,如:MVC、MTV…

    2、单例指的是一个类只能产生一个对象,可以节省空间。

    3、为什么要用单例:

    • 是为了节省空间,节省资源。
    • 当一个类的所有对象属性全部相同时则没有必要创建多个对象。
    class Single(type):
        def __call__(self, *args, **kwargs):
            if hasattr(self, 'obj'):  # 判断是否存在已经有了的对象
                return getattr(self, 'obj')  # 有就把那个对象返回
    
            obj = super().__call__(*args, **kwargs)  # 没有则创建
            print("new 了")
            self.obj = obj  # 并存入类中
            return obj
    
    class Student(metaclass=Single):
        def __init__(self, name):
            self.name = name
    
    class Person(metaclass=Single):
        pass
    
    # 只会创建一个对象
    Person() # 只会执行一次
    # new 了
    Person()
    Person()
    Person()
    Person()
    ...
    
    

    好啦,本章内容今天就分享到这里了。如果你还对后续内容感兴趣,或是对Python实例练习题、面试题、自动化软件测试感兴趣的话可以加入我们175317069一起学习。有各项学习资源发放,更有行业深潜多年的测试人技术分析讲解。期待你的加入!

    最后祝愿你能成为一名优秀的工程师!

    欢迎【评论】、【点赞】、【关注】~

    Time will tell.(时间会证明一切)

    相关文章

      网友评论

          本文标题:Python元类及单例设计模式案例解析

          本文链接:https://www.haomeiwen.com/subject/ulnbbktx.html