sqlalchemy mark-deleted 和 python 多继承下的方法解析顺序 MRO
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的更多相关文章
- Python的程序结构[2] -> 类/Class[2] -> 方法解析顺序 MRO
方法解析顺序 / MRO (Method Resolution Order) 关于方法解析顺序(MRO)的详细内容可以参考文末链接,这里主要对 MRO 进行简要的总结说明以及一些练习示例. 经典类和新 ...
- Python的方法解析顺序(MRO)[转]
本文转载自: http://hanjianwei.com/2013/07/25/python-mro/ 对于支持继承的编程语言来说,其方法(属性)可能定义在当前类,也可能来自于基类,所以在方法调用时就 ...
- python 方法解析顺序 mro
一.概要: mor(Method Resolution Order),即方法解析顺序,是python中用于处理二义性问题的算法 二义性: 1.两个基类,A和B都定义了f()方法,c继承A和B那么C调用 ...
- Python的方法解析顺序(MRO)
mro即method resolution order,主要用于在多继承时判断调的属性的路径(来自于哪个类). http://blog.csdn.net/imzoer/article/details/ ...
- Method Resolution Order – Python类的方法解析顺序
在支持多重继承的编程语言中,查找方法具体来自那个类时的基类搜索顺序通常被称为方法解析顺序(Method Resolution Order),简称MRO.(Python中查找其它属性也遵循同一规则.)对 ...
- Python中的MRO(方法解析顺序)[转载]
本文转载至: http://hanjianwei.com/2013/07/25/python-mro/ 对于支持继承的编程语言来说,其方法(属性)可能定义在当前类,也可能来自于基类,所以在方法调用时就 ...
- python面向对象双下划线方法与元类
目录 双下划线方法(__) 元类简介 产生类的两种表现形式 元类的基本使用 元类进阶操作 __new__方法 双下划线方法(__) 面向对象中的双下方法也有一些人称之为是魔法方法,有些双下方法不需要刻 ...
- python多继承下的查找顺序-MRO原则演变与C3算法
在python历史版本中的演变史 python2.2之前: MRO原则: 只有经典类,遵循深度优先(从左到右)原则, 存在的问题:在有重叠的多继承中,违背重写可用原则 解决办法是再设计类的时候不要设计 ...
- Python高级笔记(四) -- 多继承_方法解析顺序表MRO
1. 多继承以及MRO顺序 1.1 单独调用父类的方法 # -*- encoding=utf-8 -*- class Parent(object): def __init__(self, name): ...
随机推荐
- 【Java EE 学习 49 上】【Spring学习第一天】【基本配置】
一.HelloWorld 需要的jar文件(以2.5.5为例):spring.jar,common-logging.jar 1.新建类com.kdyzm.spring.helloworld.Hello ...
- MEF Parts Sample
namespace Microshaoft.MEF.Contracts { using System; public delegate void ExceptionEventHandler<TS ...
- spring-表达式语言-SpEL【转】
Spring表达式语言(Spring Expression Language)简称:SpEL 课程概要: Spring表达式语言的入门介绍 Spring表达式语言的操作范围 Spring表达式语言的运 ...
- 分布式缓存技术redis学习系列(三)——redis高级应用(主从、事务与锁、持久化)
上文<详细讲解redis数据结构(内存模型)以及常用命令>介绍了redis的数据类型以及常用命令,本文我们来学习下redis的一些高级特性. 安全性设置 设置客户端操作秘密 redis安装 ...
- HDU3333 Turing Tree(线段树)
题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=3333 Description After inventing Turing Tree, 3x ...
- MySQL 存储过程基本函数
字符串类 CHARSET(str) //返回字串字符集CONCAT (string2 [,... ]) //连接字串INSTR (string ,substring ) //返回substring首次 ...
- 今天必须完成ireport+jasperreport转成pdf
中午之前解决字体问题 2.问题总结 (1)Caused by: java.lang.NoSuchMethodException: Unknown property 'objectModelBean98 ...
- [NOIP2011]观光公交 题解
题目大意: 就省了吧 思路: 应该算是贪心. 不难发现,加速只对所有在使用加速器之后连续的一段下车时不用等人的站点下车的人有用.这非常重要. 先算出不加速时的和,并预处理出每个站点最迟到的人的时间.每 ...
- 洛谷 P1204 [USACO1.2]挤牛奶Milking Cows Label:模拟Ex 74分待查
题目描述 三个农民每天清晨5点起床,然后去牛棚给3头牛挤奶.第一个农民在300秒(从5点开始计时)给他的牛挤奶,一直到1000秒.第二个农民在700秒开始,在 1200秒结束.第三个农民在1500秒开 ...
- STM32之位绑定
有32个美女给你,你32个一起处理是不是有点棘手呀??没事..那就一个一个“慢慢”来..为何我这里慢慢要加双引号?因为其实处理一个比处理32个要快很多很多..好,停住这美好的遐想,来认识下<位绑 ...