11-元类

元类介绍 #

type可以直接生成类(class),但也可以先生成元类(metaclass),再使用元类批量定制类(class)

使用 class 创建类


class Hello():
    def say(self, name='world'):
        print('Hello, %s' % name) 
      
h = Hello()
h.say()

使用 type 直接创建


def say(self, name='world'):
    print("Hello, %s" % name)
   
Hello = type('Hello', (object, ), dict(say_hello=say))
h = Hello()
h.say()

先生成元类 metaclass,再批量创建

# 传入type
class SayMetaClass(type):

    # 传入      类名称、父类、属性
    def __new__(cls, name, bases, attrs):
        # 添加属性 
        attrs['say_'+name] = lambda self,value,saying=name: print(saying+','+value+'!')
        # 传承三大:类名称、父类、属性
        return type.__new__(cls, name, bases, attrs)

# 创建类
class Hello(object, metaclass=SayMetaClass):
    pass

# 创建实列
hello = Hello()

# 调用实例方法
hello.say_Hello('world!')  # Hello, world!   类名 + 传入的参数

class NiHao(object, metaclass=SayMetacalss):
    pass

n = NiHao()
n.say_NiHao("中国")  # NiHao, 中国!

元类的应用 #

参考文章 快速掌握元类

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__.__name__, self.name)


class StringField(Field):

    def __init__(self, name):
        super().__init__(name, 'varchar(100)')


class IntegerField(Field):

    def __init__(self, name):
        super().__init__(name, 'bigint')


class ModelMetaClass(type):
    """元类"""
    """
    创建一个新的字典mapping
    将每一个类的属性,通过.items()遍历其键值对。
    如果值是Field类,则打印键值,并将这一对键值绑定到mapping字典上。
    将刚刚传入值为Field类的属性删除。
    创建一个专门的__mappings__属性,保存字典mapping。
    创建一个专门的__table__属性,保存传入的类的名称。
    """
    def __new__(cls, name, bases, attrs):
        if name == 'Model':
            return type.__new__(cls, name, bases, attrs)
        print('Found model: %s' % name)
        mappings = dict()
        for k, v in attrs.items():
            if isinstance(v, Field):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            attrs.pop(k)
        attrs["__mappings__"] = mappings
        attrs["__table__"] = name
        return type.__new__(cls, name, bases, attrs)


class Model(dict, metaclass=ModelMetaClass):
    """创建 Modle 来自于元类"""

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

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError("'Model' object has no attribute '%s'" % key)

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

    def save(self):
        """模拟 sql 语句"""
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v.name)
            args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(str(i) for i in args))
        print("SQL: %s" % sql)
        print("Args: %s" % str(args))


class User(Model):
    """创建子类"""
    """
        id= IntegerField(‘id’)就会自动解析为:
            Model.__setattr__(self, ‘id’, IntegerField(‘id’))
            因为IntergerField(‘id’)是Field的子类的实例,自动触发元类的__new__,
            所以将IntergerField(‘id’)存入__mappings__并删除这个键值对
    """
    id = IntegerField('id')
    name = StringField('username')
    email = StringField('email')
    password = StringField('password')

"""
实例化过程中
先调用Model.__setattr__,将键值载入私有对象
然后调用元类的“天赋”,ModelMetaclass._new_,将Model中的私有对象,只要是Field的实例,都自动存入u.__mappings__
"""
# 实例化 User
u = User(id=12345, name='Batman', email='12343@qq.com', password='qwerqwer')
u.save()