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. Angular.element和$document的使用方法分析,代替jquery

    AngularJs是不直接操作DOM的,但是在平时的开发当中,我们有的时候还是需要操作一些DOM的,如果使用原生的JS的话操作过于麻烦,所以大家一般都是使用jQuery,jQuery虽然好用,但是An ...

  2. [Android]电话拨号器开发

    继续今天的Android,经过昨天大体了解了Android开发的一些基本文件结构,今天来做一个电话拨号器! 预期达到的效果 实现过程 首先还是按照昨天第一篇教程,新建一个项目叫PhoneCall的An ...

  3. 【淘淘】Quartz作业存储与管理

    一.Quartz作业管理和存储方式简介: 作业一旦被调度,调度器需要记住并且跟踪作业和它们的执行次数.如果你的作业是30分钟后或每30秒调用,这不是很有用.事实上,作业执行需要非常准确和即时调用在被调 ...

  4. Codeforces Round #FF(255) DIV2

    A - DZY Loves Hash 水题,开辟一个数组即可 #include <iostream> #include <vector> #include <algori ...

  5. linux设备驱动

    http://blog.csdn.net/bob_fly1984/article/details/8820670 struct ov5640_data {    struct ov5640_platf ...

  6. H.264的优势和主要特点

    H.264,同时也是MPEG-4第十部分,是由ITU-T视频编码专家组(VCEG)和ISO/IEC动态图像专家组(MPEG)联合组成的联合视频组(JVT,Joint Video Team)提出的高度压 ...

  7. 字体在网页中画ICON图标

    用字体在网页中画ICON图标有三种小技巧: 1.用CSS Sprite在网页中画小图标 实现方法: 首先将小图片整合到一张大的图片上 然后根据具体图标在大图上的位置,给背景定位.background- ...

  8. 【转】最近公共祖先(LCA)

    基本概念 LCA:树上的最近公共祖先,对于有根树T的两个结点u.v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u.v的祖先且x的深度尽可能大. RMQ:区间最小值查询问题.对于长度为n的 ...

  9. matlab函数大全

    Matlab 图像处理相关函数命令大全 一.通用函数: colorbar  显示彩色条 语法:colorbar \ colorbar('vert') \ colorbar('horiz') \ col ...

  10. poj1131-Octal Fractions(进制转换)

    一,题意: 求一个八进制小数的十进制.二,思路: 暴力数组模拟计算,注意千万不带小数做除法运算 1,对于八进制小数,转换成十进制,书写形式分析: 2,对其除法过程进行模拟: 3,输出. 三,步骤: 1 ...