Python的Mixins机制
大多數(shù)面向?qū)ο笳Z言都不支持多重繼承,因?yàn)檫@會(huì)導(dǎo)致菱形問題, 而 Python 雖然形式上支持多重繼承,但其實(shí)現(xiàn)機(jī)制卻是利用 mixin,從而有效 地避免了菱形問題。
一、什么是 mixin
Mixin本意是混入,程序中用來將不同功能(functionality)組合起來,從而為 類提供多種特性。而雖然繼承(inheritance)也可以實(shí)現(xiàn)多種功能,但繼承一般 有從屬關(guān)系,即子類通常是父類更加具體的類。而 mixin 則更多的是功能上的 組合,因而相當(dāng)于是接口(帶實(shí)現(xiàn)的接口)。
好比是聯(lián)想電腦與電腦之間是繼承關(guān)系,因而聯(lián)想電腦具備電腦的各種功能;而 聯(lián)想電腦與鍵盤之間則是 mixin 關(guān)系,同樣也具備鍵盤的各種功能。
一般編程語言都不允許多重繼承,主要是為了避免菱形問題,即兩個(gè)父 類如果有共同的祖父類,但對祖父類相同部分做了不同的修改,則這個(gè)類再繼承 兩個(gè)父類就會(huì)產(chǎn)生沖突。
類似于 git 版本控制中,如果兩個(gè)人對同一段代碼做了不同的修改,則合并時(shí) 就需要手動(dòng)解決沖突。編程語言如果碰到 diamond problem 時(shí)依賴程序員決定 用哪個(gè)父類的特性,就會(huì)變得非常復(fù)雜而且容易產(chǎn)生歧義。
從上面分析可以看出其實(shí)單從功能上來說,完全可以用 mixin 取代繼承,從而 可以不要類這個(gè)概念。最近幾年新出的編程語言 Rust 和Go里面就沒有類 (class)以及繼承,但并不影響代碼復(fù)用,它們也正是利用 mixin 這種機(jī)制實(shí)現(xiàn) 的代碼復(fù)用,例如 Rust 中用特征(Trait)取代了類和接口。
兩種觀點(diǎn)其實(shí)是兩種不同的世界觀,目前類與繼承的概念則更為流行,而且符合 人們對事物的認(rèn)知:人們對白貓、黑貓、花貓觀察后更容易抽象出貓的概念,而 不是將這些事物作為無規(guī)律的組合去看待。
二、Python 中的 mixin
理解了 mixin 概念之后,再將其運(yùn)用到 Python 中,理解(形式上)多重繼承 就會(huì)容易許多。python 對于 mixin 命名方式一般以 MixIn, able, ible 為后綴
由于 mixin 是組合,因而是做加法,為已有的類添加新功能,而不像繼承一樣 下一級(jí)會(huì)覆蓋上一級(jí)相同的屬性或方法,但在某些方面仍然表現(xiàn)得與繼承一樣, 例如類的實(shí)例也是每個(gè) mixin 的實(shí)例。mixin 使用不當(dāng)會(huì)導(dǎo)致類的命名空間污 染,所以要盡量避免 mixin 中定義相同方法,對于相同的方法,有時(shí)很難區(qū)分 實(shí)例到底使用的是哪個(gè)方法。
''' 遇到問題沒人解答?小編創(chuàng)建了一個(gè)Python學(xué)習(xí)交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯(cuò)的視頻學(xué)習(xí)教程和PDF電子書! ''' 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 中獲取功能并添加到 MyClassclass Myclass2(Mixin1, Mixin2):def test(self): # 已有 test 方法,因而不會(huì)再添加 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) # => TrueMixin 強(qiáng)調(diào)的是功能而不像繼承那樣包括所有功能和數(shù)據(jù)域,但利用 mixin 同 樣也可以實(shí)現(xiàn)代碼復(fù)用,下面這段代碼來自Stack Overflow,當(dāng)然 functools.total_ordering() 裝飾器已經(jīng)提供相同功能了,這里僅用來說明 mixin 實(shí)現(xiàn)代碼復(fù)用。
''' 遇到問題沒人解答?小編創(chuàng)建了一個(gè)Python學(xué)習(xí)交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯(cuò)的視頻學(xué)習(xí)教程和PDF電子書! ''' 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 <= otherdef __ge__(self, other):return self == other or self > otherclass Integer(Comparable):def __init__(self, i):self.i = iclass Char(Comparable):def __init__(self, c):self.c = c下面是 Python2 中動(dòng)態(tài)加入 mixin 的方法[fn:1],python3 中已經(jīng)不支持這種 方法了,python3 可能需要借助 type 等元編程工具實(shí)現(xiàn)[fn:2]動(dòng)態(tài) mixin
def MixIn(pyClass, mixInClass, makeLast=0):if mixInClass not in pyClass.__bases__:if makeLast:pyClass.__bases__ += (mixInClass,)else:pyClass.__bases__ = (mixInClass,) + pyClass.__bases不過盡管動(dòng)態(tài) mixin 是可能的,但實(shí)際使用中要盡量避免這樣做,因?yàn)榭赡軙?huì) 使所有使用這個(gè) mixin 的實(shí)例出現(xiàn)一些不可預(yù)知的問題。
三、Python的mixin 和 Ruby的mixin
Matthew J. Morrison提到的例子表明 Python 的 mixin 并不是純粹意義上的 mixin,還是帶有繼承的特點(diǎn)。
''' 遇到問題沒人解答?小編創(chuàng)建了一個(gè)Python學(xué)習(xí)交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯(cuò)的視頻學(xué)習(xí)教程和PDF電子書! ''' from datetime import datetime, date import jsonclass 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 = nameself.bday = bdayif __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 更準(zhǔn)確。
require "json"module Jsonabledef jsonifyjson_data = {}self.instance_variables.each do |v|json_data[v.to_s[1..-1]] = self.instance_variable_get(v)endreturn json_data.to_jsonenddef save_json(file_name)File.open(file_name, 'w') {|f| f.write(self.jsonify) }endendclass Personinclude Jsonabledef initialize(name, bday)@name = name@bday = bdayend endperson = 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總結(jié)
以上是生活随笔為你收集整理的Python的Mixins机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python:绑定方法和非绑定方法
- 下一篇: python基础教程:名称空间与作用域