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

2018-11-30  本文已影响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)
上一篇下一篇

猜你喜欢

热点阅读