sqlalchemy mark-deleted 和 python 多继承下的方法解析顺序 MRO

今天在弄一个 sqlalchemy 的数据库基类的时候,遇到了跟多继承相关的一个小问题,因此顺便看了一下 MRO

mark-deleted 在 sqlalchemy 中的实现

在做数据库的类时,由于重要的数据都不能直接删除,需要使用 mark-deleted 的方式,即在数据库中保留一个 deleted 的标记字段,根据这个标记来区分数据是否已被标记删除。被 mark-deleted 的数据,在普通查询时不能直接查询出来,但还需要支持对仅 mark-deleted 的查询,以及对所有数据的查询。不重要的数据不需要使用 mark-deleted 方式,可直接删除。

这些条件的限制经常会出现在数据库的设计要求。在 python 中这个问题相对好处理,model 的基类与 mark-deleted 的支持的类分开,mark-deleted 以 mixin 的方式,仅在需要支持 mark-deleted 的类中才使用。以下是使用 flask-sqlalchemy 的代码 ::

from myproj import db
... class Base(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
...
def save(self):
db.session.add(self)
db.session.commit() class SoftDeletedMixin(object):
deleted = db.Column(db.Boolean, default=False)
deleted_at = db.Column(db.DateTime)
...
def soft_delete(self):
self.deleted_at = datetime.datetime.utcnow()
self.deleted = True
self.save() class User(...):
username = db.Column(db.String(64), nullable=False)
password_hash = db.Column(db.String(128))
...

在 flask-sqlalchemy 中,对 Model 的扩展里面有一个 query 属性,可方便支持查询操作。支持 mark-deleted 的类,默认的 query 时需要自带 filter 为 filter_by(deleted=False),因此,需要进一步对 SoftDeletedMixin 改造。查看 flask-sqlalchemy 的 Model ,可发现 query 使用了属性描述符 property descriptor,使用了 _QueryProperty 类来描述 query 属性,而在 _QueryProperty 中需要使用 query_class 作为 query 的基类。为了支持在 query 中使用 soft_delete 方法,同时增加默认过滤,我们需要扩展这个类,或者按照它的形式编写自己的描述符。

...
class _SoftDeletedQuery(BaseQuery):
def soft_delete(self, synchronize_session='evaluate'):
return self.update({'deleted': literal_column('id'),
'updated_at': literal_column('updated_at'),
'deleted_at': timeutil.utcnow()},
synchronize_session=synchronize_session) class _SoftDeletedQueryProperty(_QueryProperty):
def __get__(self, instance, owner):
query = super(_SoftDeletedQueryProperty, self).__get__(instance, owner)
return query.filter_by(deleted=False) class _SoftDeletedQueryAllProperty(_QueryProperty):
pass class _SoftDeletedQueryDeletedProperty(_QueryProperty):
def __get__(self, instance, owner):
query = super(_SoftDeletedQueryProperty, self).__get__(instance, owner)
return query.filter_by(deleted=True)

然后,SoftDeletedMixin 修改为 ::

class SoftDeletedMixin(object):
...
query_class = _SoftDeletedQuery
query = _SoftDeletedQueryProperty(db)
query_all = _SoftDeletedQueryAllProperty(db)
query_deleted = _SoftDeletedQueryDeletedProperty(db)

接下来的一步,需要在 User 类中使用 SoftDeletedMixin ::

class User(SoftDeletedMixin, Base):
...

这里需要注意,由于 python 的多继承中对方法的搜索顺序是有约定的,不能交换 SoftDeletedMixin 和 Base 的顺序,否则就得不到正确的结果。

python MRO 和 C3 算法

多继承中方法解析顺序(Method Resolution Order,MRO),是支持多继承语言的语言必然要面对的问题,python 对 MRO 的处理在新式类(广度优先、从左到右)和经典类型(深度优先、从左到右)中的处理有所不同,见 https://www.python.org/download/releases/2.3/mro/

根据文档的说法,从 python 2.3 起,mro 采用 C3 算法。

在 python 经典类多继承中,主要面对单调性和本地优先级的问题:

单调性(monotonicity):对于继承了若干基类 AB 的类 C ,如果 C 的解析顺序为 AB ,那么 C 的所有子类的解析顺序应为 AB;

本地优先级(local precedence ordering):对于继承了 A 的类 C,如果 C 重写了 A 的方法或属性,那么 C 的所有子类访问该方法或属性时,应该优先选 C 而不是 A。

对于经典类采用深度优先算法,在 python 2.7 上,下面的代码存在本地优先级问题 ::

class A:
pass class B(A):
pass class C(A):
pass class D(B,A):
pass

通过 inspect.getmro(D) 可看到 mro 顺序为 DCAB 。那么如果 B 中重写了 A 的某个方法,在 D 中则无法访问到,因此存在本地优先级的问题。

下面的代码存在单调性问题 ::

class A:
pass class B:
pass class C(A, B):
pass class D(B, A):
pass class E(C, D):
pass

对于类 E,如果以深度优先,无论 A B 那个在前,必然会导致 C D 的任意一个单调性无法满足。通过 inspect.getmro(D) 得到的 mro 顺序为 ECABD ,可见优先级也无法满足。

新式类的 C3 算法在处理多重继承时,采用的 merge list 方法,而原理上与拓扑排序类似 http://xymlife.com/2016/05/22/python_mro/,但拓扑排序的说法不是很精确(图没有左右节点之分)。

C3 算法中,mro 是对类 C 继承层次的线性化(继承层次结构变为平坦线性结构),它采用以下的形式定义:

类列表记为 C1C2...CN ,其头部为 C1 ,尾部为 C2...CN

列表的组合 C+C1C2...CN = CC1C2...CN

类 C 继承 B1,B2,...,BN,记作 C(B1B2...BN)

类 C 的线性化(即 MRO )表示其继承结构,记作 L[C(B1B2...BN)],采用以下迭代的方法计算:

L[C(B1B2...BN)] = C + merge(L[B1]...L[BN], B1,B2...BN)
L[object] = object

其中,merge 的计算方法为:

规定:对于 merge 中的 MRO 列表,某个列表的头部不在其它列表的尾部,那么它一个好的头部;
(1)遍历 merge 中的 MRO 列表,取得一个好的头部,如果找不到则无法 merge 并抛出异常;
(2)把好的头部加到 C 的 MRO 列表 并从 merge 中的所有 MRO 列表中移除该元素;
(3)如果某个列表为空白,那么从 merge 中移除,如果 merge 中没有列表则退出并返回结果。

那么对于第一个代码,C3 算法的结果为 DBCA ;第二个代码,C3 算法在得出了 L[E]=ECD+merge(ABObject,BAObject) 后无法选头,此时会抛出异常为 ::

Traceback (most recent call last):
File "<pyshell#48>", line 1, in <module>
class E(C,D):
TypeError: Error when calling the metaclass bases
Cannot create a consistent method resolution
order (MRO) for bases A, B

sqlalchemy mark-deleted 和 python 多继承下的方法解析顺序 MRO的更多相关文章

  1. Python的程序结构[2] -> 类/Class[2] -> 方法解析顺序 MRO

    方法解析顺序 / MRO (Method Resolution Order) 关于方法解析顺序(MRO)的详细内容可以参考文末链接,这里主要对 MRO 进行简要的总结说明以及一些练习示例. 经典类和新 ...

  2. Python的方法解析顺序(MRO)[转]

    本文转载自: http://hanjianwei.com/2013/07/25/python-mro/ 对于支持继承的编程语言来说,其方法(属性)可能定义在当前类,也可能来自于基类,所以在方法调用时就 ...

  3. python 方法解析顺序 mro

    一.概要: mor(Method Resolution Order),即方法解析顺序,是python中用于处理二义性问题的算法 二义性: 1.两个基类,A和B都定义了f()方法,c继承A和B那么C调用 ...

  4. Python的方法解析顺序(MRO)

    mro即method resolution order,主要用于在多继承时判断调的属性的路径(来自于哪个类). http://blog.csdn.net/imzoer/article/details/ ...

  5. Method Resolution Order – Python类的方法解析顺序

    在支持多重继承的编程语言中,查找方法具体来自那个类时的基类搜索顺序通常被称为方法解析顺序(Method Resolution Order),简称MRO.(Python中查找其它属性也遵循同一规则.)对 ...

  6. Python中的MRO(方法解析顺序)[转载]

    本文转载至: http://hanjianwei.com/2013/07/25/python-mro/ 对于支持继承的编程语言来说,其方法(属性)可能定义在当前类,也可能来自于基类,所以在方法调用时就 ...

  7. python面向对象双下划线方法与元类

    目录 双下划线方法(__) 元类简介 产生类的两种表现形式 元类的基本使用 元类进阶操作 __new__方法 双下划线方法(__) 面向对象中的双下方法也有一些人称之为是魔法方法,有些双下方法不需要刻 ...

  8. python多继承下的查找顺序-MRO原则演变与C3算法

    在python历史版本中的演变史 python2.2之前: MRO原则: 只有经典类,遵循深度优先(从左到右)原则, 存在的问题:在有重叠的多继承中,违背重写可用原则 解决办法是再设计类的时候不要设计 ...

  9. Python高级笔记(四) -- 多继承_方法解析顺序表MRO

    1. 多继承以及MRO顺序 1.1 单独调用父类的方法 # -*- encoding=utf-8 -*- class Parent(object): def __init__(self, name): ...

随机推荐

  1. ASP.NET 一步步开发自己的博客 .NET版(11、Web.config文件的读取和修改)

    原文:http://www.cnblogs.com/zhaopei/p/5677053.html

  2. mysql 导入导出csv

    导出: select RESOURCE_ID_INT,RESOURCE_ID_CHAR,RESOURCE_TITLE from t_resource_info_wb into outfile '/us ...

  3. DOTA 2 API(个人资料)

    获取个人资料 http://wiki.teamfortress.com/wiki/WebAPI/GetPlayerSummaries 获取个人库存 http://wiki.teamfortress.c ...

  4. Node.js exports与module.exports的关系

    今天搜索module.exports时看到CNode社区上发的Hack Sparrow一篇相关文章的链接 Node.js Module – exports vs module.exports 一篇5年 ...

  5. HDU 5833 Zhu and 772002(高斯消元)

    题意:给n个数,从n个数中抽取x(x>=1)个数,这x个数相乘为完全平方数,求一共有多少种取法,结果模1000000007. 思路:每个数可以拆成素数相乘的形式,例如: x1 2=2^1 * 3 ...

  6. lua元表与元方法

    lua中提供的元表(metatable)与元方法(metamethod)是一种非常重要的语法,metatable主要用于做一些类似于C++重载操作符式的功能. lua中提供的元表是用于帮助lua变量完 ...

  7. WPF整理-为User Control添加依赖属性

    依赖属性 ".NET properties are nothing more than syntactic sugar over set and get methods." 我们知 ...

  8. bzoj 4445 [SCOI2015] 小凸想跑步

    题目大意:一个凸包,随机一个点使得其与前两个点组成的面积比与其他相邻两个点组成的面积小的概率 根据题意列方程,最后求n条直线的交的面积与原凸包面积的比值 #include<bits/stdc++ ...

  9. lunece全文检索的入门与简单优化

    2016-12-15 项目中需要加载下面几个工具包 1 package com.cn.shupu.util; import java.io.BufferedReader; import java.io ...

  10. Go-Agent部署与FQ教程(2016-10-28)

    这篇文章是为了帮助使用go-agent-crack来FQ的同学,主要是几个想用该项目来FQ的同学对github主页的FQ文档大力吐槽,索性写一篇详细的文档来支持一下大家,其中包含了google gae ...