原文:https://github.com/dengshuan/notes/blob/master/techs/python-mixins.org

https://blog.csdn.net/u012814856/article/details/81355935--------一个例子走近 Python 的 Mixin 类:利用 Python 多继承的魔力

大多数面向对象语言都不支持多重继承,因为这会导致著名的 Diamond problem, 而 Python 虽然形式上支持多重继承,但其实现机制却是利用 mixin,从而有效 地避免了 Diamond problem。

什么是 mixin

Mixin 本意是混入,程序中用来将不同功能(functionality)组合起来,从而为 类提供多种特性。而虽然继承(inheritance)也可以实现多种功能,但继承一般 有从属关系,即子类通常是父类更加具体的类。而 mixin 则更多的是功能上的 组合,因而相当于是接口(带实现的接口)。

好比是联想电脑与电脑之间是继承关系,因而联想电脑具备电脑的各种功能;而 联想电脑与键盘之间则是 mixin 关系,同样也具备键盘的各种功能。

一般编程语言都不允许多重继承,主要是为了避免 diamond problem,即两个父 类如果有共同的祖父类,但对祖父类相同部分做了不同的修改,则这个类再继承 两个父类就会产生冲突。

类似于 git 版本控制中,如果两个人对同一段代码做了不同的修改,则合并时 就需要手动解决冲突。编程语言如果碰到 diamond problem 时依赖程序员决定 用哪个父类的特性,就会变得非常复杂而且容易产生歧义。

从上面分析可以看出其实单从功能上来说,完全可以用 mixin 取代继承,从而 可以不要类这个概念。最近几年新出的编程语言 Rust 和 Go 里面就没有类 (class)以及继承,但并不影响代码复用,它们也正是利用 mixin 这种机制实现 的代码复用,例如 Rust 中用特征(Trait)取代了类和接口。

两种观点其实是两种不同的世界观,目前类与继承的概念则更为流行,而且符合 人们对事物的认知:人们对白猫、黑猫、花猫观察后更容易抽象出猫的概念,而 不是将这些事物作为无规律的组合去看待。

Python 中的 mixin

理解了 mixin 概念之后,再将其运用到 Python 中,理解(形式上)多重继承 就会容易许多。python 对于 mixin 命名方式一般以 MixIn, able, ible 为后缀

由于 mixin 是组合,因而是做加法,为已有的类添加新功能,而不像继承一样 下一级会覆盖上一级相同的属性或方法,但在某些方面仍然表现得与继承一样, 例如类的实例也是每个 mixin 的实例。mixin 使用不当会导致类的命名空间污 染,所以要尽量避免 mixin 中定义相同方法,对于相同的方法,有时很难区分 实例到底使用的是哪个方法。

class Mixin1(object):
def test(self):
print("mixin 1")
def which_test(self):
self.test() class Mixin2(object):
def test(self):
print("mixin 2") class MyClass1(Mixin1, Mixin2):
pass # 按从左到右顺序从 mixin 中获取功能并添加到 MyClass class Myclass2(Mixin1, Mixin2):
def test(self): # 已有 test 方法,因而不会再添加 Mixin1, Mixin2 的 test 方法
print("my class 2") c1 = MyClass1()
c1.test() # => "mixin 1"
c2 = MyClass2()
c2.test() # => "my class 2"
c2.which_test() # => "my class 2"
isinstance(c1, Mixin1) # => True
issubclass(MyClass1, Mixin2) # => True

Mixin 强调的是功能而不像继承那样包括所有功能和数据域,但利用 mixin 同 样也可以实现代码复用,下面这段代码来自Stack Overflow,当然 functools.total_ordering() 装饰器已经提供相同功能了,这里仅用来说明 mixin 实现代码复用。

class Comparable(object):
def __ne__(self, other):
return not (self == other) def __lt__(self, other):
return self <= other and (self != other) def __gt__(self, other):
return not self <= other def __ge__(self, other):
return self == other or self > other class Integer(Comparable):
def __init__(self, i):
self.i = i class Char(Comparable):
def __init__(self, c):
self.c = c

下面是 Python2 中动态加入 mixin 的方法[fn:1],python3 中已经不支持这种 方法了,python3 可能需要借助 type 等元编程工具实现[fn:2]动态 mixin

def MixIn(pyClass, mixInClass, makeLast=0):
if mixInClass not in pyClass.__bases__:
if makeLast:
pyClass.__bases__ += (mixInClass,)
else:
pyClass.__bases__ = (mixInClass,) + pyClass.__bases

不过尽管动态 mixin 是可能的,但实际使用中要尽量避免这样做,因为可能会 使所有使用这个 mixin 的实例出现一些不可预知的问题。

Python mixin v.s. Ruby mixin

Matthew J. Morrison 提到的例子表明 Python 的 mixin 并不是纯粹意义上的 mixin,还是带有继承的特点。

from datetime import datetime, date
import json class Jsonable(object): def date_handler(self, obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat() def save_json(self, file_name):
with open(file_name, 'w') as output:
output.write(json.dumps(self.__dict__, default=self.date_handler)) class Person(Jsonable): def __init__(self, name, bday):
self.name = name
self.bday = bday if __name__ == '__main__':
matt = Person('matt', date(1983, 07, 12))
matt.save_json("matt.json")
assert issubclass(Person, Jsonable)
assert isinstance(matt, Person)
assert isinstance(matt, Jsonable)

而 Ruby 的 Mixin 则不带有继承的概念,直接使用 include 引入 mixin。从语 义上讲,的确用 include 描述比 inherit 更准确。

require "json"

module Jsonable
def jsonify
json_data = {}
self.instance_variables.each do |v|
json_data[v.to_s[1..-1]] = self.instance_variable_get(v)
end
return json_data.to_json
end def save_json(file_name)
File.open(file_name, 'w') {|f| f.write(self.jsonify) }
end end class Person
include Jsonable
def initialize(name, bday)
@name = name
@bday = bday
end
end person = Person.new('name', '07/12/1983')
person.save_json('ruby.json')
raise "not instance" unless person.instance_of? Person
raise "is instance" if person.instance_of? Jsonable
raise "subclass" if Person.is_a? Jsonable

(转)Python Mixins 机制的更多相关文章

  1. [面向对象之继承应用(在子类派生重用父类功能(super),继承实现原理(继承顺序、菱形问题、继承原理、Mixins机制),组合]

    [面向对象之继承应用(在子类派生重用父类功能(super),继承实现原理(继承顺序.菱形问题.继承原理.Mixins机制),组合] 继承应用 类与类之间的继承指的是什么'是'什么的关系(比如人类,猪类 ...

  2. 面对对象4 Mixins机制 内置方法 反射 异常

    Mixins机制 为什么要有:子类继承父类的时候,可能会碰到需要继承多个父类的情况,那么继承关系也分主类和辅类,既保持主类的功能,也有辅类的功能. 命名方式,我们需要将主类和辅类区分开来,python ...

  3. 【Python&数据结构】 抽象数据类型 Python类机制和异常

    这篇是<数据结构与算法Python语言描述>的笔记,但是大头在Python类机制和面向对象编程的说明上面.我也不知道该放什么分类了..总之之前也没怎么认真接触过基于类而不是独立函数的Pyt ...

  4. Python反射机制理解

    Python反射机制用沛齐老师总结的话说就是:利用字符串的形式去对象(模块)中操作(寻找)成员. getattr(object, name) object代表模块,name代表模块中的属性或成员,该函 ...

  5. 理解Python命名机制

    理解Python命名机制 本文最初发表于恋花蝶的博客(http://blog.csdn.net/lanphaday),欢迎转载,但必须保留此声明且不得用于商业目的.谢谢. 引子 我热情地邀请大家猜测下 ...

  6. Python内部机制-PyObject对象

    PyObject对象机制的基石 学过Python的人应该非常清晰,Python中一切都是对象,全部的对象都有一个共同的基类,对于本篇博文来说,一切皆是对象则是探索Python的对象机制的一个入口点.我 ...

  7. 20191031:Python底层机制

    20191031:Python底层机制 python底层从3个方面来说,分别是: 引用计数机制 垃圾回收机制 内存池机制 引用计数机制 使用引用计数来追踪内存中的对象,所有对象都有引用计数,并且这个引 ...

  8. Python执行机制

    1.4 Python执行机制 Python中IDLE是其自带的集成开发工具(IDE:同时拥有编辑.编译.调试.运行等多种功能的集成工具),并且它也是Python自带的编译器和解释器. 1.4.1 Py ...

  9. python反射机制深入分析

    对编程语言比较熟悉的朋友,应该知道“反射”这个机制.Python作为一门动态语言,当然不会缺少这一重要功能.然而,在网络上却很少见到有详细或者深刻的剖析论文.下面结合一个web路由的实例来阐述pyth ...

随机推荐

  1. Tomcat服务器的安装和配置

    一.Tomcat下载 可以直接从Apache的网站上下载Tomcat(http://tomcat.apache.org/),进入首页后,在左边Download一栏可选择你要下载的版本,点击便可进入To ...

  2. Javascript 需要注意的细节

    1.使用 === 代替 ==JavaScript 使用2种不同的等值运算符:===|!== 和 ==|!=,在比较操作中使用前者是最佳实践.“如果两边的操作数具有相同的类型和值,===返回true,! ...

  3. 20155205 《Java程序设计》实验四 Android程序设计

    20155205 <Java程序设计>实验四 Android程序设计 一.实验内容及步骤 (一) Android Stuidio的安装测试 参考<Java和Android开发学习指南 ...

  4. WordPaster-KesionCMS V8整合教程

    1.上传WordPaster文件夹 2.上传ckeditor3x插件文件夹 4.修改ckeditor编辑器的config.js文件,启用插件,在工具栏中增加插件按钮 5.在文章页面增加插件初始化代码 ...

  5. redis.conf之save配置项解读

    配置示例: save 900 1 save 300 10 save 60 3600 配置解读: 1) “save 900 1”表示如果900秒内至少1个key发生变化(新增.修改和删除),则重写rdb ...

  6. VC6.0编译器设置

    主要通过VC的菜单项Project->Settings->C/C++页来完成.我们可以看到这一页的最下面Project Options中的内容,一般如下:/nologo /MDd /W3 ...

  7. MATLAB二分法函数求根

    function xc = bisect(f,a,b,tol) ind = b-a; while ind > tol xx = (a+b)/; b = xx; else a = xx; end ...

  8. How to transfer developer profile to one mac to another mac

    Export developer profile from old mac. In the Xcode Organizer, select your team in the Teams section ...

  9. noip第27课作业

    1. 繁忙的都市 [问题描述] 城市C是一个非常繁忙的大都市,城市中的道路十分的拥挤,于是市长决定对其中的道路进行改造.城市C的道路是这样分布的:城市中有n个交叉路口,有些交叉路口之间有道路相连,两个 ...

  10. android PendingIntent 使用通知传递多个参数,及不覆盖的问题

    Intent updateIntent = new Intent(GetNoticeService.this, DetailGonggaoActivity.class); updateIntent.p ...