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. 【Java EE 学习 35 上】【strus2】【类型转换器】【struts2和Servlet API解耦】【国际化问题】【资源文件乱码问题已经解决】

    一.类型转换器 1.在动作类action中,声明和表单中name属性的值同名的属性,提供get和set方法,struts2就可以通过反射机制,从页面中获取对应的内容 package com.kdyzm ...

  2. 使用FP-Growth算法高效发现频繁项集【zz】

    FP树构造 FP Growth算法利用了巧妙的数据结构,大大降低了Aproir挖掘算法的代价,他不需要不断得生成候选项目队列和不断得扫描整个数据库进行比对.为了达到这样的效果,它采用了一种简洁的数据结 ...

  3. Maven入门指南① :Maven 快速入门及简单使用

    原文链接:http://www.cnblogs.com/luotaoyeah/archive/2014/06/02/3764533.html 开发环境 MyEclipse 2014 JDK 1.8 M ...

  4. Linux第02天

    Linux 第02天 1.Linux磁盘和文件系统 VFS————虚拟文件系统 df命令————查看已挂载的分区 df 分区名 du命令————查看文件夹大小 du 文件夹名 ln命令————符号链接 ...

  5. Codeforces Round #279 (Div. 2) vector

    A. Team Olympiad time limit per test 1 second memory limit per test 256 megabytes input standard inp ...

  6. JSON字符串语法

    JSON 语法是 JavaScript 对象表示语法的子集. 数据在键/值对中展示, 多个数据由逗号分隔, 花括号保存一个对象, 方括号保存一个数组 JSON具有以下形式: 1. 对象(object) ...

  7. css3折叠效果

    在开发过程中,经常会遇到一些交互效果,今天所联系的便是一个类似折纸的折叠效果,查看效果. 说到折纸,我们先看下图 这是我第一时间想到的大体思路,如果能让这6个面连续的变化角度到0不就可以了吗,运用cs ...

  8. /usr文件系统

    /usr文件系统  /usr 文件系统经常很大,因为所有程序安装在这里. /usr 里的所有文件一般来自Linux distribution:本地安装的程序和其他东西在/usr/local 下.这样可 ...

  9. 00 EPLAN安装问题

    问题: EPLAN 安装结束后,无法创建项目,每次创建均会弹出提示框:内部错误 解决方案: 运行 Services.msc 禁用EPLAN Client Service  设置EPLAN使用管理员模式 ...

  10. HTML 5 画布(canvas)

    canvas 元素使用 JavaScript 在网页上绘制图像,本身是没有绘图能力. canvas 是一个矩形区域,可以控制其每一像素. canvas 拥有多种绘制路径.矩形.圆形.字符以及添加图像的 ...