Python中的元类MetaClass

引言

python中的元类MetaClass:本质也是一个类,但和普通类的用法不同,它可以对类内部的定义(包括类属性和类方法)进行动态的修改。

 换句话说,使用元类的主要目的是为了实现在创建类时,能够动态的改变类中定义的属性或者方法。

1、自定义元类创建类的基本方法

第一步:创建元类,一个类必须继承type才能使其成为一个元类;

第二步:创建类,使用关键词参数metaclass指定创建它的元类。

2、元类的__new__方法

 在类的创建过程中,元类的__new__方法和__init__方法会被先后调用,前者负责创建类,后者负责类的初始化。在实际使用中,大多类的初始化工作都可以放在__new__方法中进行,所以为了代码简洁往往只需要定义__new__方法。 

class FirstMetaClass(type):
    '''
    cls:代表动态修改的类
    name:代表动态修改的类名
    bases:代表被动态修改的类的所有父类
    attr:代表被动态修改的类的所有属性、方法组成的字典
    '''
    def __new__(cls, name, bases, attrs):
        attrs['name'] = 'C语言中文网'
        attrs['say'] = lambda self: print('调用say()实例方法')
        return super().__new__(cls, name, bases, attrs)
# 通过FirstMetaClass元类创建的类,会额外添加name属性和say()方法。

class CLanguage(object, metaclass=FirstMetaClass):
    pass

if __name__ == '__main__':
    clangs = CLanguage()
    print(clangs.name)
    clangs.say()

 

可以看到,在创建类时,通过元类创建的类会调用元类中的__new__方法,从而实现动态修改类属性或者类方法的目的。

3、元类创建API实例---Django ORM 

class ModelMetaClass(type):

    def __new__(cls, name, bases, attrs):
        mappings = dict()
        for k, v in attrs.items():
            # 只处理类中属性值为元组的键值对
            if isinstance(v, tuple):
                print('Found mapping: %s==>%s' % (k, v))
                mappings[k] = v

        # 处理完成后删除属性
        for k in mappings:
            attrs.pop(k)

        attrs["__mappings__"] = mappings
        attrs["__table__"] = name
        # 这里不能用type(name, bases, attrs) 直接返回创建好的类, 不会触发type的__init__方法
        return type.__new__(cls, name, bases, attrs)


class Model(metaclass=ModelMetaClass):
    # uid = ("uid", "int unsigned")
    # name = ("username", "varchar(30)")
    # email = ("email", "varchar(30)")
    # password = ("password", "varchar(30)")

    def __init__(self, **kwargs):
        # 将关键字参数添加到实例属性
        for k, v in kwargs.items():
            setattr(self, k, v)

    def save(self):
        table_name = self.__table__
        field = list()
        args = list()
        for k, v in self.__mappings__.items():
            field.append(v[0])
            arg = getattr(self, k, None)
            # ["12345","""'Michael'""","""'[email protected]'""","""'my-pwd'"""]
            if isinstance(arg, str):
                args.append("""'{}'""".format(arg))
            else:
                args.append(str(arg))

        sql = "insert into {}({}) values ({})".format(table_name, ",".join(field), ",".join(args))
        print(f"SQL语句: {sql}")


class User(Model):
    uid = ("uid", "int unsigned")
    name = ("username", "varchar(30)")
    email = ("email", "varchar(30)")
    password = ("password", "varchar(30)")


u = User(uid=12345, name="Michael", email="[email protected]", password="my-pwd")
u.save()

 

 

你可能感兴趣的:(python,python,开发语言)