• python元类深入理解


    1.python 中的类

    在python中,类也是一个对象,只不过这个对象拥有生成实例的能力,我们一般使用class XXX来定义一个类,在python解释器执行到这个地方的时候会自动创建出这个对象,python也为我们提供了手动创建类的方法,type()。type()这个方法对我们来说并不陌生,我们所熟知的用法是:class = type(instance),当传入一个参数时,type()返回这个参数的类。而今天我们要用到的是type的另一个功能。type("classname",(object,),{"name":"jiao"})。当给type传入三个参数时,就是一个手动创建类的方式。

    class A():
        def __init__(self,name):
            self.name = name
            print("创建了一个实例")
    
    a = type("a",(A,),{"name":"jiao"}) 
    print(a)              #<class '__main__.a'>
    print(a.name)          #jiao
    print(a("jiang"))      #创建了一个实例  
                            #<__main__.a object at 0x00000280A973AA58>

    type接收三个参数分别是:

    classname: 要创建的class 的名称

    object:要创建类的父类所组成的元组

    sttr_dict: 要创建类的属性

    type返回一个class,我们接收并赋值到一个变量上,现在这个变量就指向我们所创建的类,我们可以通过这个变量来使用类。

    2.python 中的type

    在python 中,几乎所有的东西都是对象,这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来——type

    3.__metaclass__属性

    python在创建类时,会按照如下的流程进行:

    Foo中有__metaclass__这个属性吗?如果是,Python会在内存中通过__metaclass__创建一个名字为Foo的类对象(我说的是类对象,请紧跟我的思路)。如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。

    那么在__metaclass__中放置什么样的代码可以创建类呢?type,或者任何使用到type或者子类化type的东东都可以。

    4.自定义元类

    class UpperAttrMetaClass(type):
        def __new__(cls,class_name,class_parents,class_attr, *args, **kwargs):
            print("__new__")
            class_attr['name'] = "jiao"
            return type.__new__(cls,class_name,class_parents,class_attr)
    
        def __init__(self,*args,**kwargs):
            print("__init__")
            super().__init__(*args, **kwargs)
            self.__cache = {}
    
        def __call__(self, *args, **kwargs):
            print("__call__")
            if args in self.__cache:
                return self.__cache[args]
            else:
                obj = super().__call__(*args)
                self.__cache[args] = obj
                return obj
    
    
    class A(metaclass=UpperAttrMetaClass):
        def __init__(self,name):
            self.name = name
            print("a.__init__")

    5.类的创建流程

    1.元类的__new__(),返回创建好的类。当我们想要改变创建方式的时候就要重写这个方法。

    2.元类的__init__(),初始化一些类的属性

    6.实例创建流程

    1.元类的__call__(),创建一个实例时,首先调用这个方法,返回创建好的实例,所以我们可以通过改写这个方法来改变实例创建过程,比如实现单例模式

    2.类的__init__(),初始化实例属性

    7.元类的应用

    1.单例模式

    class Singleton(type):
        def __init__(cls,*args,**kwargs):
            cls.__instance = None
            super().__init__(*args,**kwargs)
    
        def __call__(cls, *args, **kwargs):
            if cls.__instance is None:
                cls.__instance = super().__call__(*args,**kwargs)
                return cls.__instance
            else:
                return cls.__instance
    
    class Spam(metaclass=Singleton):
        def __init__(self):
            print("Creating Spam")
    2.缓存模式
    import weakref
    
    class Cached(type):
        def __init__(cls,*args,**kwargs):
            super().__init__(*args,**kwargs)
            cls.__cache = weakref.WeakValueDictionary()
    
        def __call__(cls, *args, **kwargs):
            if args in cls.__cache:
                return cls.__cache[args]
            else:
                obj = super().__call__(*args)
                cls.__cache[args] = obj
                return obj
    
    
    class Spams(metaclass=Cached):
        def __init__(self,name):
            print("Creating Spam({!r})".format(name))
            self.name = name

    3.获取属性的定义顺序

    能过获取到属性的定义顺序,我们就可以通过简单的方法实现属性到数据的映射,可以更加简单的将类中的属性数据化。

    from collections import OrderedDict
    
    class Typed:
        _excepted_type = type(None)
    
        def __init__(self,name=None):
            self._name = name
    
        def __set__(self, instance, value):
            if not isinstance(value,self._excepted_type):
                raise TypeError("Excepted"+str(self._excepted_type))
            instance.__dict__[self._name] = value
    
    class Integer(Typed):
        _excepted_type = int
    
    class Float(Typed):
        _excepted_type = float
    
    class String(Typed):
        _excepted_type = str
    
    class OrderedMeta(type):
    
        def __new__(cls, clsname,bases,clsdict):
            d = dict(clsdict)
            order = []
            for name,value in clsdict.items():
                if isinstance(value,Typed):
                    value._name = name
                    order.append(name)
                    d['_order'] = order
           return type.__new__(cls,clsname,bases,d)
    
        @classmethod
        def __prepare__(metacls, name, bases):
            return OrderedDict()
    
    #注:__prepare__该方法会在类定义一开始的时候调用,调用时以类名和基类名称作为参数,它必须返回一个映射对象,供处理类定义体时调用
    
    
    #eg.
    class Structure(metaclass=OrderedMeta):
    
        def as_csv(self):
            return ','.join(str(getattr(self,name)) for name in self._order)
    
    class Stock(metaclass=OrderedMeta):
        name = String()
        shares = Integer()
        price = Float()
        def __init__(self,name,shares,price):
            self.name = name
            self.shares = shares
            self.price = price
    
    s = Stock("haha",23,23.3)
    print(s.name)
    s = Stock(34,23,34)
    # print(s.as_csv())
     

    8.小结

    元类主要就是在类和实例创建的时候发挥作用,来实现一些功能。

  • 相关阅读:
    盒子跟随鼠标移动而移动(兼容IE8)
    JavaScript捕获鼠标坐标
    JavaScript判断滚动条是否滚动到底部
    JavaScript自定义getStyle方法获取元素样式
    JavaScript实现简单的图片切换功能
    SpeedReader
    JavaScript定时器
    远程登陆ubantu服务器 .bashrc文件每次打开终端都需要source的问题
    阿里云服务器Ubantu16.04升级python3.6
    阿里云服务器ubantu创建新用户登录显示问题
  • 原文地址:https://www.cnblogs.com/jiaojianglong/p/11260944.html
Copyright © 2020-2023  润新知