zoukankan      html  css  js  c++  java
  • 小白尝试写一篇类元编程记录。

    Java学了几个小时,这两天又被元编程搞死,准备粗粗写一些我的理解。后面还有协程需要理解。感觉年底之前搞定这些有点累。

    先上参考文献:https://www.liaoxuefeng.com/wiki/1016959663602400/1017592449371072

    https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python/6581949#6581949

    https://www.jianshu.com/p/224ffcb8e73e

    觉的我写的垃圾的,可以看链接的,都试大神级别写的。

    首先,我们在定义一个普通的类的时候,一般都用class,后面一个类名就可以了,但Python万物皆对象,那我们的类又是谁创造的呢?或者说我们的类是哪个类爸爸实例出来的。

    In [332]: class Demo: 
         ...:     pass 
         ...:                                                                                                     
    
    In [333]: Demo.__class__                                                                                      
    Out[333]: type
    

     从代码可以明显看出来,类是由类爸爸创建的实例,类爸爸的实例就试普通的类。

    所以我们class 创建类的时候,其实是调用了类爸爸的函数type

    In [335]: Demo_f = type('Demo',(),{})                                                                         
    
    In [336]: Demo_f                                                                                              
    Out[336]: __main__.Demo
    
    In [337]: Demo_f.__class__                                                                                    
    Out[337]: type
    

     type如果用来实例化创建类的话,里面需要三个参数第一个类名,第二个是继承的父类,第三个是参数用字典的形式可以传入函数,也可以直接传入变量。

    传入函数就好比class 里面的 def:后面的函数变量名,k的变量名,v的定义的函数。如果传入一个普通的值的话,就像普通的类属性,但话说class里面定义的函数也只不过是一个可以调用的属性而已。

    我这里来写一个稍微复杂一点,既要继承父类属性的,又需要初始化的类。

    def __init__(self, name):
        self.name = name
    
    New_List = type('New_List', (list,), {'__init__': __init__, 'show': lambda self: self.name})
    
    new_list = New_List('sidian')
    print(new_list.show())
    new_list.append(12)
    print(new_list)
    
    sidian
    [12]
    

     从type定义可以看到,该类继承了list的所有属性,然后有自定义了几个方法,一个初始化的方法,一个lamdba方法。

    metaclass

    除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。

    metaclass,直译为元类,简单的解释就是:

    当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。

    后面我将复制廖雪峰大神的代码,逐行进行解释说明,这也对我的orm模型的创建有了更加深入的了解。

    先上一个我自己写的最简单的例子:

    class ModeMetaClass(type):
        def __new__(cls, *args, **kwargs):
            print(args, kwargs)        # 正常这个args就包含name, bases, attrs三个元素
            return type.__new__(cls, *args)
    
    class Demo(list, metaclass=ModeMetaClass):
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
            super(Demo, self).__init__()
    
        def show(self):
            return (f'my name is {self.name},age is {self.age}')
    
    demo = Demo('sidian', 66)
    print(demo.show())
    print(type(demo))
    print(Demo.__mro__)
    ('Demo', (<class 'list'>,), {'__module__': '__main__', '__qualname__': 'Demo', '__init__': <function Demo.__init__ at 0x10afc4200>, 'show': <function Demo.show at 0x10afc4290>, '__classcell__': <cell at 0x10afc6090: empty>}) {}
    my name is sidian,age is 66
    <class '__main__.Demo'>
    (<class '__main__.Demo'>, <class 'list'>, <class 'object'>)
    

     第一条输出其实在我没有实例化Demo就已经输出了,因为这是在创造类Demo的时候就需要输出了,所以它是最早执行的。

    整个代码还是比较简单的,自定义了一个元类,但我除了输出,没有增加任何的功能。

    但从ModeMetaClass输出可以看到Demo内部的说有属性都成为了ModeMetaClass元类attrs参数。

    下面将上廖雪峰大神的代码:

    # -*- coding: utf-8 -*-
    
    class ModelMetaclass(type):
        # 这就是通过三个参数接收需要通过元类创建类的对象的属性
        def __new__(cls, name, bases, attrs):
            print(name,bases,attrs)
            if name=='Model':        # 判断一下,如果是Model创建类,不经过修改使用原来的type创建
                return type.__new__(cls, name, bases, attrs)
            print('Found model: %s' % name)
            # 这里就不是Model类的情况下,创建类了
            mappings = dict()        # 定义一个字典
            for k, v in attrs.items():       # 将需要创建的类属性复制给k, v
                # 将v进行判断是不是Field的实例,因为需要创建的User里面4个字段的实例类的父类为Field
                if isinstance(v, Field):
                    print('Found mapping: %s ==> %s' % (k, v))
                    mappings[k] = v
            # 将本来attrs里面的参数中,删除value是Fiels实例的字段,
            # 假如不删除,通过__getattr__可能会由冲突。
            for k in mappings.keys():
                attrs.pop(k)
            # 将属性中带有实例方法的字段放入__mappings__中
            attrs['__mappings__'] = mappings # 保存属性和列的映射关系
            attrs['__table__'] = name # 假设表名和类名一致
            return type.__new__(cls, name, bases, attrs)
    
    class Model(dict, metaclass=ModelMetaclass):
        # 基础父类dict初始化
        def __init__(self, **kw):
            super(Model, self).__init__(**kw)
        # 设置__getattr__,可以通过.来取出对象的属性值
        def __getattr__(self, key):
            try:
                return self[key]
            except KeyError:
                raise AttributeError(r"'Model' object has no attribute '%s'" % key)
        # 通过__setattr__,可以通过.取出来的值,然后直接赋值。
        def __setattr__(self, key, value):
            self[key] = value
    
        def save(self):
            fields = []
            params = []
            args = []
            for k, v in self.__mappings__.items():
                # fields列表中,添加User类属性中的实例的属性name
                fields.append(v.name)
                params.append('?')
                # 这个self,User调用刚好可以返回属性参数
                args.append(getattr(self, k, None))
            sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
            print('SQL: %s' % sql)
            print('ARGS: %s' % str(args))
    
    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(StringField, self).__init__(name, 'varchar(100)')
    
    class IntegerField(Field):
    
        def __init__(self, name):
            super(IntegerField, self).__init__(name, 'bigint')
    
    
    
    class User(Model):
        # 定义类的属性到列的映射,这里的属性在使用元类创建是,将成为元类attrs的参数
        # 没个属性都是一个实例.
        id = IntegerField('id')
        name = StringField('username')
        email = StringField('email')
        password = StringField('password')
    
    
    # 创建一个实例:
    u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
    print(u.name)
    # print(u.sex)
    print(u.__mappings__)
    print(u.__class__)
    # 保存到数据库:
    u.save()
    Model (<class 'dict'>,) {'__module__': '__main__', '__qualname__': 'Model', '__init__': <function Model.__init__ at 0x11031d320>, '__getattr__': <function Model.__getattr__ at 0x11031d3b0>, '__setattr__': <function Model.__setattr__ at 0x11031d440>, 'save': <function Model.save at 0x11031d4d0>, '__classcell__': <cell at 0x11031b490: empty>}
    User (<class '__main__.Model'>,) {'__module__': '__main__', '__qualname__': 'User', 'id': <__main__.IntegerField object at 0x11031b7d0>, 'name': <__main__.StringField object at 0x11031b810>, 'email': <__main__.StringField object at 0x11031b850>, 'password': <__main__.StringField object at 0x11031b890>}
    Found model: User
    Found mapping: id ==> <IntegerField:id>
    Found mapping: name ==> <StringField:username>
    Found mapping: email ==> <StringField:email>
    Found mapping: password ==> <StringField:password>
    Michael
    {'id': <__main__.IntegerField object at 0x11031b7d0>, 'name': <__main__.StringField object at 0x11031b810>, 'email': <__main__.StringField object at 0x11031b850>, 'password': <__main__.StringField object at 0x11031b890>}
    <class '__main__.User'>
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'column_type', 'name']
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'column_type', 'name']
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'column_type', 'name']
    ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'column_type', 'name']
    SQL: insert into User (id,username,email,password) values (?,?,?,?)
    ARGS: [12345, 'Michael', 'test@orm.org', 'my-pwd']
    

     整个分析来看,代码写的真漂亮,在User里面不定义任何方法,所有的方法都是从父类调用,父类的创建由元类创建,自身也可以调用父类的元类创建类。

    这个写代码的风格还是我需要努力学习的,我梦想有生之年做一个架构师,觉得这些代码的学习对我的帮助还是非常大的。

  • 相关阅读:
    Dubbo 配置参数
    类文件结构

    shell script 编程入门
    OnePlus5刷 TWRP
    TimeUtil 工具类
    Outline 科学的上网
    HotSpot虚拟机的锁优化
    equals与hashCode
    Java中的动态代理
  • 原文地址:https://www.cnblogs.com/sidianok/p/11973701.html
Copyright © 2011-2022 走看看