小白尝试写一篇类元编程记录。

Posted sidianok

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小白尝试写一篇类元编程记录。相关的知识,希望对你有一定的参考价值。

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 (fmy 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里面不定义任何方法,所有的方法都是从父类调用,父类的创建由元类创建,自身也可以调用父类的元类创建类。

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

 

 

以上是关于小白尝试写一篇类元编程记录。的主要内容,如果未能解决你的问题,请参考以下文章

python小白学习记录 多线程爬取ts片段

小白学习大数据测试之ETL

Python爬虫个人记录利用Python在豆瓣上写一篇日记

编程小白入门

asp.net core的AOP记录

小白从零开始学编程--python安装与环境搭建