Python之元类

type生元类,元类生类,类生对象

换句话就是

 

道系元类解读,生就完了

话不多说上代码来理解:

def fn1(self,name='world'):
    print('Hello,%s'%name)
def fn2(self,name='world'):
    print('Hi,%s'%name)

Hello = type('Hello',(object,),dict(say_hi=fn1,hi='hello'))
Hi = type('Hello',(object,),dict(say_hi=fn2,hi='hi'))
# 生成Hello类的对象
hello1 = Hello()
hello1.say_hi()
print(hello1.hi)
print(hello1)

print('-'*50) # 华丽分割线
# 生成Hi类的对象 hello2 = Hi() print(hello2.hi) hello2.say_hi() print(hello2)

结果:

Hello,world
hello
<__main__.Hello object at 0x0000022EBA06EBA8>
--------------------------------------------------
hi
Hi,world
<__main__.Hello object at 0x0000022EBA06EC50>

type()可以产生类,那么结果就证明了,他可以定制类的名称属性方法等。可以用于创造万物。

django中的ORM,大致思路如下:

class Field(object):
    def __init__(self, column_type, primary_key, default):
        self.column_type = column_type
        self.primary_key = primary_key
        self.default = default


class StringField(Field):
    def __init__(self, column_type='varchar(32)', primary_key=False, default=None):
        super().__init__(column_type, primary_key, default)


class IntegerField(Field):
    def __init__(self,column_type='int', primary_key=False, default=0):
        super().__init__(column_type, primary_key, default)


class MyMetaClass(type):
    def __new__(cls, class_name,class_bases,class_attrs):
        if class_name == 'Models':
            return type.__new__(cls,class_name,class_bases,class_attrs)
        table_name = class_attrs.get('table_name',class_name)
        primary_key = None
        mappings = {}

        for k,v in class_attrs.items():
            if isinstance(v,Field):
                mappings[k]=v
                if v.primary_key:
                    if primary_key:
                        raise TypeError('一张表只能有一个主键')
                    primary_key = k

        print(class_attrs)

        for k in mappings.keys():
            class_attrs.pop(k)
        if not primary_key:
            raise TypeError('一张表必须有主键')
        class_attrs['table_name'] = table_name
        class_attrs['primary_key'] = primary_key
        class_attrs['mappings'] = mappings

        print(class_attrs)

        return type.__new__(cls, class_name,class_bases,class_attrs)

class Models(dict,metaclass=MyMetaClass):

    def __init__(self,**kwargs):
        super().__init__(**kwargs)

    def __getattr__(self, item):
        return self.get(item,'没有该键')

    def __setattr__(self, key, value):
        self[key] = value

    @classmethod
    def select(cls,**kwargs):
        pass

if __name__ == '__main__':
    class Teacher(Models):
        table_name = 'author'
        id = IntegerField(primary_key=True)
        name = StringField()

    a = Teacher(id=12,name='xxx')
    print(a)
    print(a.name)

结果:

{'__module__': '__main__', '__qualname__': 'Teacher', 'table_name': 'author', 'id': <__main__.IntegerField object at 0x0000021E7680ECF8>, 'name': <__main__.StringField object at 0x0000021E76814438>}
{'__module__': '__main__', '__qualname__': 'Teacher', 'table_name': 'author', 'primary_key': 'id', 'mappings': {'id': <__main__.IntegerField object at 0x0000021E7680ECF8>, 'name': <__main__.StringField object at 0x0000021E76814438>}}
{'id': 12, 'name': 'xxx'}
xxx

可以理解为:元类metaclass,是继承了type来控制其他类的产生的工具

然后用MyMetaClass来控制继承Model类的Teacher类,在生成类的实例的过程中,将类的同名属性id、name打包进mappings属性中,使得可以通过getattr来获取他自身字典中的值。可能还是不太明白那就继续看:

class Models(dict):

    def __init__(self,**kwargs):
        super().__init__(**kwargs)

    def __getattr__(self, item):
        return self.get(item,'没有该键')

    def __setattr__(self, key, value):
        self[key] = value

    @classmethod
    def select(cls,**kwargs):
        pass

if __name__ == '__main__':
    class Teacher(Models):
        table_name = 'author'
        id = IntegerField(primary_key=True)
        name = StringField()

    a = Teacher(id=12,name='xxx')
    print(a)
    print(a.name)

结果:

{'id': 12, 'name': 'xxx'}
<__main__.StringField object at 0x000001BD9825EDD8>

此时没有元类MyMetaClass的控制,a.name,获取到的是Teacher类的属性,是一个StringField的对象,而不是他本身字典的键对应值,问题就是这里,元类的引入就是为了解决这种问题,因为a.name会首先找本类的属性,再找父类的属性,如果在这过程中找到了,就不会继续查询,也根本不会调用重写的__getattr__方法。所以在类的创建时,通过把原先的属性隐藏起来,可以让对象去触发getattr得到自身包含的值。

type产生类

# 类是type的对象,可以通过type(参数)来创建类

# type(name, bases, namespace)

s = '''
my_a = 10
my_b = 20
def __init__(self):
    pass
@classmethod
def print_msg(cls, msg):
    print(msg)
'''
namespace = {}
exec(s, {}, namespace)

Student = type('Student', (object, ), namespace)

stu = Student()

 

 自定义元类

# 元类:所有自定义的类本身也是对象,是元类的对象,所有自定义的类本质上是由元类实例化出来了
Student = type('Student', (object, ), namespace)

class MyMeta(type):
    # 在class Student时调用:Student类的创建 => 来控制类的创建
    
    # 自定义元类,重写init方法的目的:
    # 1.该方法是从type中继承来的,所以参数同type的init
    # 2.最终的工作(如果开辟空间,如果操作内存)还是要借助type
    # 3.在交给type最终完成工作之前,可以对类的创建加以限制 *****
    def __init__(cls, class_name, bases, namespace):
        # 目的:对class_name | bases | namespace加以限制 **********************
        super().__init__(class_name, bases, namespace)
    
    # 在Student()时调用:Student类的对象的创建 => 来控制对象的创建
    
    # 自定义元类,重写call方法的目的:
    # 1.被该元类控制的类生成对象,会调用元类的call方法
    # 2.在call中的返回值就是创建的对象
    # 3.在call中
    #       -- 通过object开辟空间产生对象
    #       -- 用被控制的类回调到自己的init方法完成名称空间的赋值
    #       -- 将修饰好的对象反馈给外界
    def __call__(cls, *args, **kwargs):
        # 目的:创建对象,就可以对对象加以限制 **********************
        obj = object.__new__(cls)  # 通过object为哪个类开辟空间
        cls.__init__(obj, *args, **kwargs)  # 调回当前被控制的类自身的init方法,完成名称空间的赋值
        return obj

# 问题:
# 1.继承是想获得父级的属性和方法,元类是要将类的创建于对象的创建加以控制
# 2.类的创建由元类的__init__方法控制
#        -- 元类(class_name, bases, namespase) => 元类.__init__来完成实例化
# 3.类的对象的创建由元类的__call__方法控制
#         -- 对象产生是需要开辟空间,在__call__中用object.__new__()来完成的
class Student(object, metaclass=MyMeta):
    pass

# class Student:  <=>  type(class_name, bases, namespace)

单例

# 单例:一个类只能产生一个实例
# 为什么要有单例:
# 1.该类需要对象的产生
# 2.对象一旦产生,在任何位置再实例化对象,只能得到第一次实例化出来的对象
# 3.在对象唯一创建后,可以通过属性修改或方法间接修改属性,来完成数据的更新,不能通过实例化方式更新数据

 

posted @ 2019-05-30 18:40  MartinV  阅读(469)  评论(0编辑  收藏  举报