• python 类的内置函数2


    必须明确创建对象的过程: 先创建空对象,执行初始化将属性存储到对象的名称空间中!
    所以在__call__函数中必须完成这两步操作,同时将初始化完成的对象返回给调用者
    
    一旦覆盖了__call__函数,就必须自己来完成上述的几个步骤
    
    class MyMate(type):
        def __call__(self, *args, **kwargs):
            # 创建空对象
            # 调用init
            # 返回初始化后的对象
            obj = object.__new__(self)
            self.__init__(obj,*args,**kwargs)
            return obj
    class Foo(metaclass=MyMate):
        def __init__(self):
            print("初始化对象")
    f = Foo()
    print(f)
    通过元类来控制一个类实例化对象的过程
    只需覆盖__call__函数我们就能完成对实例化过程的控制
    
    #需求:
    #2.要求实例化时传参必须为关键字形式,否则抛出异常TypeError: must use keyword argument
    #3.key作为用户自定义类产生对象的属性,且所有属性变成大写
    class Mymetaclass(type):
        def __call__(self, *args, **kwargs):
            if args:
                raise TypeError('must use keyword argument for key function')
            obj = object.__new__(self) #创建对象,self为类Chinese
    
            for k,v in kwargs.items():
                obj.__dict__[k.upper()]=v
            return obj
    class Chinese(metaclass=Mymetaclass):
        country='China'
        tag='Legend of the Dragon' #龙的传人
        def walk(self):
            print('%s is walking' %self.name)
    p=Chinese(name='egon',age=18,sex='male')
    print(p.__dict__)
    补充:
    产生类Teacher的过程就是在调用Mymeta,而Mymeta也是type类的一个对象,那么Mymeta之所以可以调用,一定是实现了__call__方法,但是我们就算自己写该方法,类也可以被创建,这是因为type中已经有默认的__call__的实现了
    该方法中同样需要做至少三件事
    
    #伪代码
     class type:
         def __call__(self, *args, **kwargs): #self=<class '__main__.Mymeta'>
             obj=self.__new__(self,*args,**kwargs) # 产生Mymeta的一个对象
             self.__init__(obj,*args,**kwargs) 
             return obj
    5.元类实现单例
    什么是单例
    单例是指的是单个实例,指一个类只能有一个实例对象
    
    为什么要用单例
    当一个类的实例中的数据不会变化时使用单例,数据是不变的
    
    例如开发一个音乐播放器程序,音乐播放器可以封装为一个对象,那你考虑一下,当你切歌的时候,是重新创建一个播放器,还是使用已有的播放器?
    
    因为播放器中的数据和业务逻辑都是相同的没有必要创建新的,所以最好使用单例模式,以节省资源
    
    当两个对象的数据完全相同时 则没有必要占用两份资源
    
    #使用classmethod 实现单例
    class Player():
        def __init__(self):
            print("创建播放器了")
        __play = None
        @classmethod
        def get_player(cls):
            if not cls.__play:
                cls.__play = Player()
            return cls.__play
    
    
    p1 = Player.get_player();
    p1 = Player.get_player();
    p1 = Player.get_player();
    p1 = Player.get_player();
    该方法无法避免使用者直接调用类来实例化,这样就不是单例了
    
    使用元类实现单例模式
    
    #在类定义时 自动执行init 在init中创建实例 call中直接返回已有实例
    class MyMeta(type):
        __instance = None
    
        def __init__(self,name,bases,dic):
            if not self.__instance:
                self.__instance = object.__new__(self)
                self.__init__(self.__instance)
    
            super().__init__(name, bases, dic)
    
    
        def __call__(cls):
            return cls.__instance
    
    class Player(metaclass=MyMeta):
        def __init__(self):
            print("创建播放器了")
    Player()
    Player()
    # 仅执行一次创建播放器
    6.元类之属性查找
    当一个类既有父类又有元类时属性的查找顺序是什么样的?
    
    回顾一下,在没有元类时属性的查找是基于MRO列表的顺序,这个点还是相同的,那我们为某个类增加元类后,元类中的属性,什么时候会被使用到呢?来看一个例子
    
    class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
        n=444
        def __new__(cls, *args, **kwargs):
            pass
    class Bar(object):
        n = 333
        def __new__(cls, *args, **kwargs):
            pass
    class Foo(Bar):
        n=222
        def __new__(cls, *args, **kwargs):
            pass
    class Teacher(Foo,metaclass=Mymeta):
        n=111
        def __new__(cls, *args, **kwargs):
            pass
        school='Tsinghua'
    print(Teacher.__new__)
    print(Teacher.n)
    测试结果表明:属性查找的顺序依然是遵循MRO列表顺序,当顶级类object中不存在时会查找元类,元类没有时查找元类的父类也就是type类,
    
    image-20181204102324732

    image-20181204102324732

    7.令人迷惑的__new__函数与__init__函数
    class M(type):
        def __init__(self,clsname,bases,namespace):
            print("init")
        def __call__(self, *args, **kwargs):
            pass
        pass
    class A(metaclass=M):
        n = 1
        pass
    print(A.__name__)
    print(A.__bases__)
    print(A.__dict__)
    """输出
    init
    A
    (<class 'object'>,)
    {'__module__': '__main__', 'n': 1, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
    """
    我们已经知道__init__可以控制类的创建过程,但是现在我们看到的是,init中没有任何代码但是类的三个基本信息已经都有了,这说明类的创建其实已经完成了
    
    class M(type):
        def __new__(cls, *args, **kwargs):
            print("new")
            #return type.__new__(cls,*args,**kwargs)
        def __init__(self,clsname,bases,namespace):
            print("init")
    class A(metaclass=M):
        n = 1
    print(A.__name__)
    print(A.__bases__)
    print(A.__dict__)
    """输出
    new
    Traceback (most recent call last):
      File "/Users/jerry/PycharmProjects/元类属性查找.py", line 43, in <module>
        print(A.__name__)
    AttributeError: 'NoneType' object has no attribute '__name__'"""
    执行了__new__函数但是并没有执行__init__,因为__new__函数是真正用于创建类的方法,只有创建类成功了才会执行init函数,new必须要有返回值且返回值类型为__type__时才会执行__init__函数,
    
    将__new__中被注释的代码打开 一切正常! 再一次印证了第四节中的伪代码
    
    总结:元类中__new__是用于创建类对象的 __init__是用于初始化类的其他信息的
  • 相关阅读:
    冗余换性能从Backbone的triggerEvents说开了去
    Sublime Text3 中安装 Emmet
    windows下安装dig
    掺合模式(Mixin)
    Backbone.sync将模型同步到服务器
    Sublime text jQuery插件
    快捷键汇集
    动态创建script在IE中缓存js文件时导致编码不正确bug
    Firefox中使用location.hash会自动decodeURI Bug
    Backbone事件模块
  • 原文地址:https://www.cnblogs.com/Sunbreaker/p/11299483.html
Copyright © 2020-2023  润新知