美文网首页
python中的元类和abc.ABCMeta实现的虚类

python中的元类和abc.ABCMeta实现的虚类

作者: 还少一双小脚丫 | 来源:发表于2018-11-30 12:11 被阅读0次

    一、元类
    要理解元类,需要先理解python中的类,用class修饰的都可以叫做类,例如

    class Class():
        pass
    c = Class()
    print(c)
    <__main__.Class object at 0x00000221E277EBE0>
    

    而在python中远远不止于此,众所周知在python中“一切”皆是对象,注意是“一切”,也就是说类本身也是一个对象,你可以直接打印类本身,例如

    Class
    Out[45]: 
    __main__.Class
    

    你可以为类本身增加属性

    c = Class()
    Class.b = 2
    c.b
    Out[51]: 
    2
    

    我们平时用的类都是实例化以后的类,可以在任何时候动态的创建类,通常情况我们都是这样c=Class(),python解释器会将它认为是创建类,可是解释器本身是如何创建类的,答案是利用type
    type平时我们可能认为是查看对象的类型,例如

    type(c)
    Out[52]: 
    __main__.Class
    

    那么Class的类型是什么呢?

    type(Class)
    Out[53]: 
    type
    

    看到没,Class的类型是type
    我们可以用type直接生成一个类
    type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

    type('Class_type',(),{'a':1,'b':2})
    Out[55]: 
    __main__.Class_type
    Class_type = type('Class_type',(),{'a':1,'b':2})
    c_t = Class_type()
    c.a
    Out[58]: 
    1
    

    到这里,可以引入元类了,什么是元类
    元类就是用来创建这些类(对象)的类,元类就是类的类
    type就是所有类的元类,可以理解为所有的类都是由type创建的
    我们也可以创建自己的元类,这个类需要继承自type

    __metaclass__属性
    当我们定义了__metaclass__属性的时候,解释器会做些什么呢
    例如我们定义了

    class Test(Base):
         pass
    

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

    那么__metaclass__是啥东西呢?
    答案就是可以创建类的东西,前面提过,类是由type创建的,所以__metaclass__内部一定要返回一个类,它可以是一个函数,也可以是一个类,而这个类就是我们自定义的元类,这个类必须继承自type
    如果是个函数:例如我们想把类的所有属性变成大写

    def upper_attr(future_class_name,future_class_parent,future_class_attr):
        attrs = ((name,value) for name,value in future_class_attr.items() if not name.startswith('__'))
        uppercase_attr = dict((name.upper(),value) for name,value in attrs)
        #通过type创建类对象
        return type(future_class_name,future_class_parent,uppercase_attr)
    class Test(object,metaclass=upper_attr):
        a = '1'
    

    当然我们也可以定义自己的元类来实现这一动作

    class UpperAttrMetaClass(type):
        def __new__(upperattr_metaclass,future_class_name,future_class_parent,future_class_attr):
            attrs = ((name,value) for name,value in future_class_attr.items() if not name.startswith('__'))
            uppercase_attr = dict((name.upper(),value) for name,value in attrs)
            return type.__new__(upperattr_metaclass,future_class_name,future_class_parent,uppercase_attr)
           #return super(UpperAttrMetaClass,upperattr_metaclass).__new__(upperattr_metaclass,future_class_name,future_class_parent,uppercase_attr)
    
    

    看起来有些负载,这些参数都是啥
    我们看看type的文档

        def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
            """
            type(object_or_name, bases, dict)
            type(object) -> the object's type
            type(name, bases, dict) -> a new type
            # (copied from class doc)
            """
            pass
    

    type(name, bases, dict) -> a new type就是创建我们的类的方法,name是要创建的类的名字,base是要创建类的基类,dict是类的属性,正是因为有这个属性,我们才可以对类的创建自定义各种动作

    通常元类用来创建API是非常好的选择,使用元类的编写很复杂但使用者可以非常简洁的调用API
    著名的Django的ORM用的就是元类

    class Field(object):
        def __init__(self,name,column_type):
            self.name = name
            self.column_type = column_type
        def __str__(self):
            return '<%s:%s>' % (self.__class__,self.name)
    
    class StringField(Field):
        def __init__(self,name):
            super(StringField,self).__init__(name,'varchar(100)')
    
    class IntField(Field):
        def __init__(self,name):
            super(IntField,self).__init__(name,'bigint')
    
    class ModelMetaclass(type):
        def __new__(cls, name, bases,attrs):
            if name == 'Model':
                return type.__new__(cls,name,bases,attrs)
            print('Found model :%s' % name)
            mapping = {}
            for k,v in attrs.items():
                if isinstance(v,Field):
                    print('Found mapping:%s ==> %s' %(k,v))
                    mapping[k] = v
            for k in mapping.keys():
                attrs.pop(k)
            attrs['__mappings__'] = mapping
            attrs['__table__'] = name
            return type.__new__(cls,name,bases,attrs)
    
    class Model(dict,metaclass=ModelMetaclass):
        def __init__(self,**kw):
            super(Model,self).__init__(**kw)
    
        def __getattr__(self, item):
            try:
                return self[item]
            except KeyError:
                raise AttributeError('Model object ha no attribute: %s' % item)
    
        def __setattr__(self, key, value):
            self[key] = value
    
        def save(self):
            fields = []
            params = []
            args = []
            for k,v in self.__mappings__.items():
                fields.append(v.name)
                params.append('?')
                args.append(self[k])
                #args.append(getattr(self,k,None))
            sql = 'insert into %s (%s) values (%s)' %(self.__table__,','.join(fields),','.join(params))
            print('SQL:%s' % sql)
            print('ARGS: %s' % str(args))
    
    class User(Model):
        id = IntField('id')
        name = StringField('username')
        email = StringField('email')
        password = StringField('pwd')
    

    直接说可能不好理解,拿这份代码去调试一下,就什么都懂了,代码从廖雪峰网上抄的

    下面再用元类来实现一个单例模式
    一般我们可以用重写__new__来实现单例

    class Singleton(object):
        def __new__(cls, *args, **kwargs):
            if not hasattr(cls,'_instance'):
                cls._instance = super(Singleton,cls).__new__(cls,*args,**kwargs)
            return cls._instance
    
    s1 = Singleton()
    s2 = Singleton()
    print('singleton',s1 is s2)
    

    那么用元类该如何实现

    class MetaSingleton(type):
        def __init__(self,*args,**kwargs):
            self._instance = None
            super(MetaSingleton,self).__init__(*args,**kwargs)
    
        def __call__(self, *args, **kwargs):
            if self._instance is None:
                self._instance = super(MetaSingleton,self).__call__(*args,*kwargs)
            return self._instance
    
    class Singleton1(object,metaclass=MetaSingleton):
        pass
    
    s1 = Singleton1()
    s2 = Singleton1()
    print('singleton1',s1 is s2)
    

    很多人可能看不懂了,为啥这里要用__init__,__call__又是干啥的,其实一开始我也被弄乱了,后来想了想就明白了,__new__是类被创建的时候执行的,注意是类创建,而不是类实例的创建,类的创建是在模块被导入的时候就创建好的,后面我们用到的其实都是类的实例,那么还记得之前我们说的元类的作用吗,它是用来创建类的,注意是类(又强调一次)不是类的实例。当我们在执行创建类实例的时候执行的是__call__的方法,正常情况是这样

    class test(object):
        def __init__(self):
            print('init')
        def __call__(self, *args, **kwargs):
            print('call')
        def __new__(cls, *args, **kwargs):
            print('new')
            return super(test,cls).__new__(cls,*args,**kwargs)
    
    t = test()
    t()
    

    看上去有些奇怪,t是个实例,怎么能执行呢,python就是这样,如果定义了__call__就可以这样(记住这个__call__是属于这个类实例对象的),这时候我们再回去,还记得type创建的是个什么吗,它创建了一个类(它是type实例化后的对象),在我们实例化这个类的时候就会执行这个类的__call__

    二、abc.ABCMeta
    简单的说ABCMeta就是让你的类变成一个纯虚类,子类必须实现某个方法,这个方法在父类中用@abc.abstractmethod修饰
    例如

    import abc
    class Base(object,metaclass=abc.ABCMeta):
    
        @abc.abstractmethod
        def func_a(self,data):
            '''
            :param data:
            :return:
            '''
        @abc.abstractmethod
        def func_b(self,data,out):
            '''
            :param data:
            :param out:
            :return:
            '''
        def func_d(self):
            print('func_d in base')
    

    你可以实现这两个虚方法,也可以不实现
    这样在Base的子类中就必须实现func_a,func_b2个函数,否则就会报错

    class Sub(Base):
    
        def func_a(self,data):
            print('over write func_a',data)
    
        def func_b(self,data,out):
            print('over write func_b')
    

    如果还想调用虚类的方法用super

        def func_b(self,data,out):
            super(Sub,self).func_b(data,out)
            print('over write func_b')
    

    还有一种方法是,注册虚子类

    class Register(object):
        def func_c(self):
            print('func_c in third class')
    
        def func_a(self,data):
            print('func_a in third class',data)
    Base.register(Register)
    

    这样调用issubclass(), issubinstance()进行判断时仍然返回真值(不知道这有啥用)
    三、python中定义泛型方法
    需要用到functools.singledispatch

    
    #泛型方法
    @functools.singledispatch
    def fun(arg,verbose = False):
        if verbose:
            print('Let me just say:',end=' ')
        print(arg)
    #重载方法
    @fun.register(int)
    def _(arg,verbose = False):
        if verbose:
            print('accept int param:',end=' ')
        print(arg)
    
    fun('123',True)
    fun(123,True)
    

    相关文章

      网友评论

          本文标题:python中的元类和abc.ABCMeta实现的虚类

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