小白尝试写一篇类元编程记录。
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 (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里面不定义任何方法,所有的方法都是从父类调用,父类的创建由元类创建,自身也可以调用父类的元类创建类。
这个写代码的风格还是我需要努力学习的,我梦想有生之年做一个架构师,觉得这些代码的学习对我的帮助还是非常大的。
以上是关于小白尝试写一篇类元编程记录。的主要内容,如果未能解决你的问题,请参考以下文章