Python之元类

 

一 什么是元类

 ①元类是类的类,是类的模板;

 ②元类是用来控制如何创建类的,正如类是创建对象的模板一样;

 ③元类的实例为类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象;

#创建类的第一种方式
class Foo:
    def __init__(self,name):
        self.name=name
print(Foo)
print(Foo.__dict__)

#创建类的第二种方式
def __init__(self,name,age):
    self.name=name
    self.age=age
def test(self):
    print('这是test方法')
#type需要传三个参数:①类名,②父类(可以有多个父类,所以是个元组)③所创建类的属性和方法
FFo=type('FFo',(object,),{'x':1,'__init__':__init__,'test':test})
print(FFo)
print(FFo.__dict__)
f=FFo('AA',18)
f.test()
创建类的两种方式

二 自定义元类控制类的创建

 ①一个类没有声明自己的元类,默认它的元类就是type.除了使用内置元类type,也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类;

 ②自定义元类可以控制类的产生过程,类的产生过程其实就是元类的调用过程即FFo=Mymeta('FFo',(object,),{...}),调用Mymeta会先产生一个空对象FFo,然后连同调用Mymeta括号内的参数一同传给Mymeta下的__init__方法,完成初始化,如下所示:

 

class Mymeta(type):#只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    def __init__(self,class_name,class_base,class_dic):
        # print(class_name)#FFo
        # print(class_base)#(<class 'object'>,)
        # '''
        # {'__module__': '__main__', '__qualname__': 'FFo', '__doc__': '\n    注释文档\n    ',
        #  '__init__': <function FFo.__init__ at 0x000001ADD9106730>}
        # '''
        # print(class_dic)
        super(Mymeta,self).__init__(class_name,class_base,class_dic)#重用父类功能
        if class_name.islower():
            raise TypeError('类名%s请修改为驼峰体'%class_name)
        if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) ==0:
            raise TypeError('类中必须有文档注释,并且文档注释不能为空')

class FFo(object,metaclass=Mymeta):#FFo=Mymeta('FFo',(object,),{...})
    '''
    注释文档
    '''
    school='郑大'
    def __init__(self,name,age):
        self.name=name
        self.age=age
元类初始化过程

 三 自定义元类控制类的调用

   可以在元类中增加__call__方法,改写__call__方法的逻辑,从而控制调用FFo的过程,比如将FFo的对象中的所有属性变成私有的.

具体思路可以看: https://www.cnblogs.com/linhaifeng/articles/8029564.html

 

class Mymeta(type):#只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    def __call__(self,*args,**kwargs):
        #1,调用__new__产生一个对象obj
        obj=self.__new__(self)#此处self是类FFo,必须传参,代表创建一个FFo的对象obj相当于object.__new__(self)
        #2 调用__init__初始化空对象obj
        self.__init__(obj,*args,**kwargs)#相当于FFo.__init__()
        # 在初始化之后,obj.__dict__里就有值了
        obj.__dict__={'_%s__%s'%(self.__name__,k):v for k,v in obj.__dict__.items()}

        #3返回初始化好的对象obj
        return obj


class FFo(object,metaclass=Mymeta):#FFo=Mymeta('FFo',(object,),{...})
    '''
    注释文档
    '''
    school='郑大'
    def __init__(self,name,age):
        self.name=name
        self.age=age
f=FFo('AA',18)
print(f.__dict__)
将FFo的对象中的属性变成私有的

 

class Mymetaclass(type):
    def __new__(cls,name,bases,attrs):
        update_attrs={}
        for k,v in attrs.items():
            if not callable(v) and not k.startswith('__'):
                update_attrs[k.upper()]=v
            else:
                update_attrs[k]=v
        return type.__new__(cls,name,bases,update_attrs)#将Chinese这个类返回

class Chinese(metaclass=Mymetaclass):
    country='China'
    tag='Legend of the Dragon' #龙的传人
    def walk(self):
        print('%s is walking' %self.name)


print(Chinese.__dict__)
'''
{'__module__': '__main__',
 'COUNTRY': 'China', 
 'TAG': 'Legend of the Dragon',
 'walk': <function Chinese.walk at 0x0000000001E7B950>,
 '__dict__': <attribute '__dict__' of 'Chinese' objects>,                                         
 '__weakref__': <attribute '__weakref__' of 'Chinese' objects>,
 '__doc__': None}
'''
在元类中把自定义类的数据属性都变成大写
'''
1.元类帮其完成创建对象,以及初始化操作;
2.要求实例化时传参必须为关键字形式,否则抛出异常TypeError: must use keyword argument
3.key作为用户自定义类产生对象的属性,且所有属性变成大写
'''

class Mymetaclass(type):
    # def __new__(cls,name,bases,attrs):
    #     update_attrs={}
    #     for k,v in attrs.items():
    #         if not callable(v) and not k.startswith('__'):
    #             update_attrs[k.upper()]=v
    #         else:
    #             update_attrs[k]=v
    #     return type.__new__(cls,name,bases,update_attrs)

    def __call__(self, *args, **kwargs):
        if args:
            raise TypeError('must use keyword argument for key function')
        obj = object.__new__(self) #创建对象,self为类Foo

        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__)
在元类中控制自定义类无需__init__方法
#步骤五:基于元类实现单例模式
# 单例:即单个实例,指的是同一个类实例化多次的结果指向同一个对象,用于节省内存空间
# 如果我们从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了
#settings.py文件内容如下
HOST='1.1.1.1'
PORT=3306

#方式一:定义一个类方法实现单例模式
import settings

class Mysql:
    __instance=None
    def __init__(self,host,port):
        self.host=host
        self.port=port

    @classmethod
    def singleton(cls):
        if not cls.__instance:
            cls.__instance=cls(settings.HOST,settings.PORT)
        return cls.__instance


obj1=Mysql('1.1.1.2',3306)
obj2=Mysql('1.1.1.3',3307)
print(obj1 is obj2) #False

obj3=Mysql.singleton()
obj4=Mysql.singleton()
print(obj3 is obj4) #True



#方式二:定制元类实现单例模式
import settings

class Mymeta(type):
    def __init__(self,name,bases,dic): #定义类Mysql时就触发

        # 事先先从配置文件中取配置来造一个Mysql的实例出来
        self.__instance = object.__new__(self)  # 产生对象
        self.__init__(self.__instance, settings.HOST, settings.PORT)  # 初始化对象
        # 上述两步可以合成下面一步
        # self.__instance=super().__call__(*args,**kwargs)


        super().__init__(name,bases,dic)

    def __call__(self, *args, **kwargs): #Mysql(...)时触发
        if args or kwargs: # args或kwargs内有值
            obj=object.__new__(self)
            self.__init__(obj,*args,**kwargs)
            return obj

        return self.__instance




class Mysql(metaclass=Mymeta):
    def __init__(self,host,port):
        self.host=host
        self.port=port



obj1=Mysql() # 没有传值则默认从配置文件中读配置来实例化,所有的实例应该指向一个内存地址
obj2=Mysql()
obj3=Mysql()

print(obj1 is obj2 is obj3)

obj4=Mysql('1.1.1.4',3307)



#方式三:定义一个装饰器实现单例模式
import settings

def singleton(cls): #cls=Mysql
    _instance=cls(settings.HOST,settings.PORT)

    def wrapper(*args,**kwargs):
        if args or kwargs:
            obj=cls(*args,**kwargs)
            return obj
        return _instance

    return wrapper


@singleton # Mysql=singleton(Mysql)
class Mysql:
    def __init__(self,host,port):
        self.host=host
        self.port=port

obj1=Mysql()
obj2=Mysql()
obj3=Mysql()
print(obj1 is obj2 is obj3) #True

obj4=Mysql('1.1.1.3',3307)
obj5=Mysql('1.1.1.4',3308)
print(obj3 is obj4) #False
单例模式

 

 

 

 

 

posted on 2019-12-03 19:57  rwwh  阅读(257)  评论(0)    收藏  举报

导航