用类的继承关系试着梳理一下ORM的实现,以及为什么要用attr.pop(k)的原因
Topic source另外评论区好像以为save()里面的getattr是model里定义的那个,其实那个getattr是python的内置函数,看参数格式就看出来区别了,
后者是getattr(obj,attr),返回该属性的值
前者是getattr(key),返回的是该键的值
相比起来前者少个obj位置参数
啊不对,是我搞错了,getattr相当于覆写了getattr方法
根据是否注释pop(k)和def getattr这两个操作列个矩阵
1:两个都注释,args返回为ARGS: [<__main__.IntegerField object at 0x000001CB37837460>, <__main__.StringField object at 0x000001CB37837400>,
相当于把id = integerfield('id)中的integerfield(‘id’)作为getattr的返回值放入args了,这是没删去定义域的缘故,此时调用的getattr是内置函数getattr
2,两个都不注释, args 返回为ARGS: [10115, 'Michael', 'test@orm.org', 'my-pwd']
这时候getattr返回的是以id为键的对应值10115,符合预期,此时调用的getattr是覆写的getattr,因为没有属性等等,所以内置函数getattr跑不通,由自定义的getattr顶上
3,只注释def getattr ,args返回为[None, None, None, None],这时调用的是内置函数getattr(定义的getattr注释掉了),因为没有对应属性(被pop删了),所以返回为默认值None
4,只注释pop(k), args返回为ARGS: [<__main__.IntegerField object at 0x000001CB37837460>, <__main__.StringField object at 0x000001CB37837400>,这时调用的是内置函数getattr
说明在两个getattr都能跑通的时候优先调用的是内置的getattr,这时候直接用u['id']可以拿到值,只是定义的getattr没有运行
那这样就有个比较神奇的地方,迭代里的getattr是有三个参数(self,k,None),但是自定义的getattr只有两个(self,key)
结果运行自定义getattr(self,k,None)的时候居然没问题
关于getattr使用和pop的原因,楼主可能解释有失偏颇,__getattr__不是复写了getattr的方法,显然两者的参数也不一样。
1. getattr方法:
获取实例属性,如果没有实例属性则获取实例对应类的属性【这里是实验得到的,没有查官网,和楼主说的一样~】。如果类和实例都不具备该属性,python自动调用特殊函数__getattr__。
2. __getattr__:
官网描述为Called when the default attribute access fails with an AttributeError
(either __getattribute__()
raises an AttributeError
because name is not an instance attribute or an attribute in the class tree for self
; or __get__()
of a name property raises AttributeError
). This method should either return the (computed) attribute value or raise an AttributeError
exception.
也就是说在获取不到实例属性和类属性后,getattr会raise AttributeError。随后python会将属性名传入__getattr__,若用户定义类时重写了__getattr__,则按照用户定义的方式返回结果。
根据官网要求,__getattr__要么返回结果,要么继续raise AttributeError。
3. 此处的用法:
首先metaclass在__new__函数中更改的均为类属性,而非实例属性。其次在定义User时,也是定义的类属性。所以User对应的实例暂时没有实例属性。
如果在__new__中没有删除User定义时的attrs,则获取User实例的属性时,会返回对应类属性。【因为Field只定义了__str__,在运行时返回的具体Field信息,像这样:<__main__.IntegerField object at 0x000001E941E13B00>]
如果删除了User定义时的attrs,即使用attr.pop(k),按照getattr调用的规定,最终会调用自定义的__getattr__方法.
4.在__getattr__中self[key]的使用:
因为Model继承自dict,在初始化时将实例属性保存为字典,因此实例(self)有字典的方法,如__getitem__,所以可以通过self[key]调用.
- 1
alienation
首先ORM作为结果是为了将SQL的表结构映射为在python更直观的类-对象结构
有如下的对应关系:
表<——>某一类
表行<——>该类的一个实例
表列<——>该类的某一个属性
表某一行的某一列元素<——>该类某一实例对应属性的一个特殊值
(表间关系这里没讲我也不知道咋映射的)
所以最前端的类是属性使用的数据类型类xxxField
这些类都是由object-Field这个树继承下来的
然后所有这些数据类型类xxxField都以嵌套方式用在被映射为类的表(如User)中
实现表列<——>类的属性的映射
接着是User类的继承树
User—Model—(metaclass=Modelmetaclass)—dict
User的父类是Model和dict,dict作为最底端的类给所有由表映射而来的类提供了一般字典方法
Model作为基类为所有由表映射而来的类(如User)提供了getattr、setattr、save三个一般方法
然后就是元类modelmetaclass了,根据mro方法,这里的modelmetaclass其实不是后续model和user的父类
他只是劫持了这几个类的创建过程,往里加了一个过滤属性的机制并且把合法的属性转换成了字典映射(不合法的不进入映射),删去了用来确定合法属性的属性
新增了mapping和table两个映射方法,然后以这两个为真正的方法返回到创建类的过程,所以user真正的方法其实是mapping和table
看mapping的具体实现