元类

1 什么是元类:

源自一句话:在python中,一切皆对象,而对象都是由类实例化得到的

# class OldboyTeacher:
#     def __init__(self,name,age,sex):
#         self.name=name
#         self.age=age
#         self.sex=sex
#
#     def score(self):
#         print('%s is scoring' %self.name)

# tea1=OldboyTeacher('egon',18,'male')
# # print(type(tea1))
# print(type(OldboyTeacher))

对象tea1是调用OldboyTeacher类得到的,如果说一切皆对象,那么OldboyTeacher也是一个对象,只要是对象

# 都是调用一个类实例化得到的,即OldboyTeacher=元类(...),内置的元类是type

# 关系:

   1. 调用元类---->自定义的类

   2. 调用自定义的类---->自定义的对象

 class关键字创建自定义类的底层的工作原理,分为四步

   1. 先拿到类名:'OldboyTeacher'

   2. 再拿到类的基类们:(object,)

   3. 然后拿到类的名称空间???(执行类体代码,将产生的名字放到类的名称空间也就是一个字典里,补充exec)

   4. 调用元类实例化得到自定义的类

OldboyTeacher=type('OldboyTeacher',(object,),{...})
# class OldboyTeacher: #OldboyTeacher=type(...)
#     school = 'Oldboy'
#     def __init__(self,name,age,sex):
#         self.name=name
#         self.age=age
#         self.sex=sex
#
#     def score(self):
#         print('%s is scoring' %self.name)
# print(OldboyTeacher)

 

 自定义类的三个关键组成部分:

 1. 类名

 2. 类的基类们

 3. 类的名称空间

 

 不依赖class关键字创建一个自定义类

# 1. 拿到类名
class_name='OldboyTeacher'
#2. 拿到类的基类们:(object,)
class_bases=(object,)
#3. 拿到类的名称空间
class_dic={}
class_body="""
school = 'Oldboy'

def __init__(self,name,age,sex):
    self.name=name
    self.age=age
    self.sex=sex

def score(self):
    print('%s is scoring' %self.name)
"""
exec(class_body,{},class_dic)
print(class_dic)
#4. 调用type得到自定义的类
OldboyTeacher=type(class_name,class_bases,class_dic)

print(OldboyTeacher)
# print(OldboyTeacher.school)
# print(OldboyTeacher.score)

tea1=OldboyTeacher('egon',18,'male')
print(tea1.__dict__)

 

#总结:对象之所以可以调用,是因为对象的类中有一个函数__call__
#推导:如果一切皆对象,那么OldboyTeacher也是一个对象,该对象之所可以调用,肯定是这个对象的类中也定义了一个函数__call__

 

class Mymeta(type): #但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
    def __call__(self, *args, **kwargs): #self=OldboyTeacher这个类,args=('egon',18,'male'),kwargs={}
        # 1. 先产生一个空对象
        tea_obj=self.__new__(self) #tea_obj是OldboyTeacher这个类的对象
        # 2. 执行__init__方法,完成对象的初始属性操作
        self.__init__(tea_obj,*args,**kwargs)
        # 3. 返回初始化好的那个对象
        return tea_obj

class OldboyTeacher(object,metaclass=Mymeta): #OldboyTeacher=Mymeta('OldboyTeacher',(object,),{...})
    school = 'Oldboy'

    #            tea_obj,'egon',18,'male'
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex

    def score(self):
        print('%s is scoring' %self.name)

tea1=OldboyTeacher('egon',18,'male') # 会触发OldboyTeacher的类(即元类)中的__call__

 实例化会

   1. 先产生一个空对象

   2. 执行__init__方法,完成对象的初始属性操作

   3. 返回初始化好的那个对象

 

 

基于元类实现单例模式 

#步骤五:基于元类实现单例模式
# 单例:即单个实例,指的是同一个类实例化多次的结果指向同一个对象,用于节省内存空间
# 如果我们从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了
#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
View Code

 

posted @ 2018-08-27 19:51  薛才昌  阅读(150)  评论(0编辑  收藏  举报