python經典類和新式類
❶ python2.7.3和3.3.2的區別
轉自:http://my.oschina.net/chihz/blog/123437
這邊只說明面向對象方面的,其他方面見上面鏈接
面向對象
(1) 經典類和新式類
Python OO最神奇的地方就是有兩種類,經典類和新式類。
新式類跟經典類的差別主要是以下幾點:
1. 新式類對象可以直接通過__class__屬性獲取自身類型:type
2. 繼承搜索的順序發生了改變,經典類多繼承屬性搜索順序: 先深入繼承樹左側,再返回,開始找右側;新式類多繼承屬性搜索順序: 先水平搜索,然後再向上移動
3. 新式類增加了__slots__內置屬性, 可以把實例屬性的種類鎖定到__slots__規定的范圍之中。
4. 新式類增加了__getattribute__方法
Python 2.x中默認都是經典類,只有顯式繼承了object才是新式類
Python 3.x中默認都是新式類,不必顯式的繼承object
python 2.x:
>>> ClassicClass.__class__
Traceback (most recent call last):
File "<stdin>", line 1, in <mole>
AttributeError: class ClassicClass has no attribute '__class__'
>>> class NewClass(object):
... pass
...
>>> NewClass.__class__
python 3.x:
>>> class NewClass:pass
...
>>> NewClass.__class__
<class 'type'>
(2) 無綁定方法
在Python 2.x中除了類方法和靜態方法,其餘的方法都必須在第一個參數傳遞self跟實例綁定,但是在Python 3.x中廢除了這條規定,允許方法不綁定實例,這樣的方法跟普通的函數沒有區別:
Python 2.x:
>>> class MyClass:
... def function():
... print "function"
...
>>> MyClass.function()
Traceback (most recent call last):
File "<stdin>", line 1, in <mole>
TypeError: unbound method function() must be called with MyClass instance as first argument (got nothing instead)
>>> m = MyClass()
>>> m.function()
Traceback (most recent call last):
File "<stdin>", line 1, in <mole>
TypeError: function() takes no arguments (1 given)
Python 3.x:
>>> class MyClass:
... def function():
... print("function")
...
>>> MyClass.function()
function
>>> m = MyClass()
>>> m.function()
Traceback (most recent call last):
File "<stdin>", line 1, in <mole>
TypeError: function() takes no arguments (1 given)
(3) 重要的重載
1. next()和__next__():這應該是繼print之後第二大坑爹的不兼容吧,Python程序漫山遍野都是迭代器,但是2和3之間迭代器的實現介面方法名是不同的……嗯,啥都不說了。
2. 分片攔截:Python 3.x之前, 類可以定義__getslice__和__setslice__方法來專門攔截分片,並且這兩個方法優先於__getitem__和__setitem__, 但是Python 3.x時代這倆方法再也不存在了,全部的工作都交給了__getitem__和__setitem__,因此在使用這兩個方法之前要先判斷傳遞進參數的類型是不是slice對象。
3. __bool__方法:我們知道Python中默認將所有的空對象定義為布爾意義上的False,在自己定義的類中我們也可以加入自定義的布爾判斷標准,在2.x中這個方法名叫做__nonzero__, 這個名字顯然非常不直觀並且不科學!所有考試交白卷的孩子我們都要否定他們的才能么?顯然不能!因此Python 3.x中這個方法被重名命為__bool__
4. 3.x 取消了用於大小比較的__cmp__方法,取而代之的是:__lt__、__gt__、__le__、__ge__、__eq__、__ne__,嗯,我感覺這個想法真是不能苟同……有誰能說服我給我洗腦讓我愛上這一堆__lt__、__gt__、__le__、__ge__、__eq__、__ne__么。。。
(4) 類修飾器
在我的上一篇博客中秀了一把函數裝飾器在表單驗證中的使用,http://my.oschina.net/chihz/blog/122897
在3.x的時代,類也有裝飾器了,這個裝飾器威力巨大,能把裝飾的類搞的面目全非,總之想怎麼搞就怎麼搞,用法同函數裝飾器基本一致,只不過傳遞的參數是類型:
>>> def shutdown(cls):
... def shutdown_func(self):
... print("do something...")
... cls.shutdown = shutdown_func
... return cls
...
>>> @shutdown
... class Test:pass
...
>>> t = Test()
>>> t.shutdown()
do something...
異常
先來看一段代碼
python 2.x:
>>> class Person:
... def __init__(self, msg):
... self.msg = msg
...
>>> try:
... raise Person, "woca"
... except Person as p:
... print p.msg
...
woca
python 3.x:
>>> class Person:
... def __init__(self, msg):
... self.msg = msg
...
>>> try:
... raise Person("woca")
... except Person as p:
... print(p.msg)
Traceback (most recent call last):
File "<stdin>", line 2, in <mole>
TypeError: exceptions must derive from BaseException
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 3, in <mole>
TypeError: catching classes that do not inherit from BaseException is not allowed
>>>
接下來說不同:
1. 在2.x時代,所有類型的對象都是可以被直接拋出的,在3.x時代,只有繼承自BaseException的對象才可以被拋出。
2. 2.x raise語句使用逗號將拋出對象類型和參數分開,3.x取消了這種奇葩的寫法,直接調用構造函數拋出對象即可。
在2.x時代,異常在代碼中除了表示程序錯誤,還經常做一些普通控制結構應該做的事情,在3.x中可以看出,設計者讓異常變的更加專一,只有在錯誤發生的情況才能去用異常捕獲語句來處理。
❷ python中①class a: pass ②class a(): pass ③class a(object): pass 這三種有什麼區別嗎
沒有區別,只是語法比較自由,類默認就是繼承於 object對象的。
❸ python入門需要學哪些
初學者學習Python需循序漸進,可以從以下內容入手學習:
1.Python基礎知識
學習任何一門編程語言都需要學習相關語法知識,Python基礎知識的學習主要包括Python解釋器執行原理、字元編碼、注釋、變數、縮進、流程式控制制、文件操作、數據類型、數據類型內置方法、字元串格式化、運算符、輸入輸出、三元運算、collections、列表、字典、元組、集合、IO操作、文件增刪改查、函數等。
Python資料庫、緩存、隊列學習內容為Python操作redis、Python操作memcache、rabbitMQ消息隊列、資料庫介紹、mysql資料庫安裝使用、mysql管理、mysql數據類型、常用mysql命令、創建資料庫、外鍵、增刪改查表、許可權、事務、索引、Python操作mysql等。
6.Web開發基礎
Python之Web開發基礎學習內容為HTML基礎、CSS基礎、JavaScript基礎、局部變數和全局變數、集合、數組、字典、函數參數、原型、面向對象、作用域、dom編程、jquery介紹、jquery選擇器、jquery屬性和CSS操作、jquery文檔處理、jquery篩選、jquery事件託管、jquery事件、jquery ajax、jquery擴展方法、bootstrap使用、EasyUI介紹和使用等。
7.Web框架學習
Python之Web框架學習內容為Web框架本質、socket伺服器、基於反射的路由系統、WSGI介凱閉行紹及原理實現態御、開發自己的Web框架、MVC和MTV、路由系統、模板、django基礎學習與使用、普通路由和動態路由、模板引擎、ORM介紹、Django ORM增刪改查學習、自定義tag、django進階學習與使用、模型綁定、Form表單驗證、Django ORM進階學習、ModelForm、自定義Validator等。
❹ 又來求助了,大神求解答 python類繼承的問題
老式類就是經典類,不是繼承自object類.在多繼承時採用深度優先遍歷父類.
新式類就是基類繼承自object類 class xxx(object).多繼承時採用一種新的C3 演算法來遍歷父類.
實例如下:
新式類的打配絕印結果如下:
1speak: I am mother舊式類的列印結果如下:
1speak: I am GrandFather由此我們可以看出新式類的搜索過程為:Son-->Father-->Mother,而舊式類的搜索過程為:Son-->Father-->GrandFather
我們可以看出舊式類和我們預期的繼承不太一樣。
老式類就是經典類,不是繼承自object類.在多繼承時採用深度優先遍歷父類.
新式類就是基類繼承自object類 class xxx(object).多繼承時採用一種新的C3 演算法來遍歷父類.
為什麼採用C3演算法呢?
C3演算法最早被提出是用於Lisp的,應用在Python中是為了解決原來基於深度優先搜索演算法不滿足本地優先順序,和單調性的問題。
本地優先順序:指聲明時父類的順序,比如C(A,B),如果培虧姿訪問C類對象屬性時,應該根據聲明順序,優先查找A類,然後再查找B類。
單調性:如果在C的解析順序中,A排在B的前面,那麼在C的所有子類里,也必須滿足這個順序。
為了解釋C3演算法,我們引入了mro(mro即 method resolution order (方法解釋順序),主要用於在多繼承時判斷屬性的路徑(來自於哪個類))。
我們可以通過class.mro()來查看python類的mro
C3演算法
判斷mro要先確定一個線性序列,然後查找路徑由由序列中類的順序決定。所以C3演算法就是生成一個線性序列。
如果繼承至一個基類:
class B(A)
這時B的mro序列為[B,A]
如果繼承至多個基類
class B(A1,A2,A3 ...)
這時B的mro序列 mro(B) = [B] + merge(mro(A1), mro(A2), mro(A3) ..., [A1,A2,A3])
merge操作就是C3演算法的核心。
遍歷執行merge操作的序列,如果一個序列的第一個元素,是其他序列中的第一個元素,或不在其他序列出現,則從所有執行merge操作序列中刪除這個元素,合並到當前的mro中。
merge操作後的序列,繼續執行merge操作,直到merge操作的序列為空。
如果merge操作的序列無法為空,則說明不合法。
例子:
class A(O):pass
class B(O):pass
class C(O):pass
class E(A,B):pass
class F(B,C):pass
class G(E,F):pass
A、B、C都繼承至一個基類,所以mro序列依次為[A,O]、[B,O]、[C,O]
mro(E) = [E] + merge(mro(A), mro(B), [A,B])
= [E] + merge([A,O], [B,O], [A,B])
執行merge操作的序列為[A,O]、[B,O]、[A,B]
A是序列[A,O]中的第空拿一個元素,在序列[B,O]中不出現,在序列[A,B]中也是第一個元素,所以從執行merge操作的序列([A,O]、[B,O]、[A,B])中刪除A,合並到當前mro,[E]中。
mro(E) = [E,A] + merge([O], [B,O], [B])
再執行merge操作,O是序列[O]中的第一個元素,但O在序列[B,O]中出現並且不是其中第一個元素。繼續查看[B,O]的第一個元素B,B滿足條件,所以從執行merge操作的序列中刪除B,合並到[E, A]中。
mro(E) = [E,A,B] + merge([O], [O])
= [E,A,B,O]
❺ Python類的多重繼承問題深入分析
Python類的多重繼承問題深入分析
首先得說明的是,Python的類分為經典類 和 新式類
經典類是python2.2之前的東西,但是在2.7還在兼容,但是在3之後的版本就只承認新式類了
新式類在python2.2之後的版本中都可以使用
經典類和新式類的區別在於:
經典類是默認沒有派生自某個基類的,而新式類是默認派生自object這個基類的:
代碼如下:
# old style
class A():pass
# new style
class A(obejct):pass
2.經典類在類多重繼承的時候是採用從左到右深度優先原則匹配方法的..而新式類是採用C3演算法(不同於廣度優先)進行匹配的
3.經典類是沒有__MRO__和instance.mro()調用的,而新式類是有的.
為什麼不用經典類,要更換到新式類
因為在經典類中的多重繼承會有些問題...可能導致在繼承樹中的方法查詢繞過後面的父類:
代碼如下:
class A():
def foo1(self):
print "A"
class B(A):
def foo2(self):
pass
class C(A):
def foo1(self):
print "C"
class D(B, C):
pass
d = D()
d.foo1()
按照經典類的查找順序從左到右深度優先的規則,在訪問d.foo1()的時候,D這個類是沒有的..那麼往上查找,先找到B,裡面沒有,深度優先,訪問A,找到了foo1(),所以這時候調用的是A的foo1(),從而導致C重寫的foo1()被繞過.
所以python引入了新式類的廳扒塌概念,每個基類都繼承自object並且,他的匹配此姿規則也從深度優先換到扮圓了C3
C3演算法
C3演算法是怎麼做匹配的呢..在問答版塊上面討論之後,歸結如下:
C3演算法的一個核心是merge.
在merge列表中,如果第一個序列mro的第一個類是出現在其它序列,並且也是第一個,或者不出現其它序列,那麼這個類就會從這些序列中刪除,並合到訪問順序列表中
比如:(引用問題中zhuangzebo的回答@zhuangzebo)
代碼如下:
class A(O):pass
class B(O):pass
class C(O):pass
class D(A,B):pass
class E(C,D):pass
首先需要知道 O(object)的mro(method resolution order)列表是[O,]
那麼接下來是:
代碼如下:
mro(A) = [A, O]
mro(B) = [B, O]
mro(C) = [C, O]
mro(D) = [D] + merge(mro(A), mro(B), [A, B])
= [D] + merge([A, O], [B, O], [A, B])
= [D, A] + merge([O], [B, O], [B])
= [D, A, B] + merge([O], [O])
= [D, A, B, O]
mro(E) = [E] + merge(mro(C), mro(D), [C, D])
= [E] + merge([C, O], [D, A, B, O], [C, D])
= [E, C] + merge([O], [D, A, B, O], [D])
= [E, C, D] + merge([O], [A, B, O])
= [E, C, D, A, B] + merge([O], [O])
= [E, C, D, A, B, O]
然後還有一種特殊情況:
比如:
merge(DO,CO,C) 先merge的是D
merge(DO,CO,C) 先merge的是C
意思就是.當出現有 一個類出現在兩個序列的頭(比如C) 這種情況和 這個類只有在一個序列的頭(比如D) 這種情況同時出現的時候,按照順序方式匹配。
新式類生成的訪問序列被存儲在一個叫MRO的只讀列表中..
你可以使用instance.__MRO__或者instance.mro()來訪問
最後匹配的時候就按照MRO序列的順序去匹配了
C3和廣度優先的區別:
舉個例子就完全明白了:
代碼如下:
class A(object):pass
class B(A):pass
class C(B):pass
class D(A):pass
class E(D):pass
class F(C, E):pass
按照廣度優先遍歷,F的MRO序列應該是[F,C,E,B,D,A]
但是C3是[F,E,D,C,B,A]
意思是你可以當做C3是在一條鏈路上深度遍歷到和另外一條鏈路的交叉點,然後去深度遍歷另外一條鏈路,最後遍歷交叉點
新式類和經典類的super和按類名訪問問題
在經典類中,你如果要訪問父類的話,是用類名來訪問的..
代碼如下:
class A():
def __init__(self):
print "A"
class B(A):
def __init__(self):
print "B"
A.__init__(self) #python不會默認調用父類的初始化函數的
這樣子看起來沒三問題,但是如果類的繼承結構比較復雜,會導致代碼的可維護性很差..
所以新式類推出了super這個東西...
代碼如下:
class A():
def __init__(self):
print "A"
class B(A):
def __init__(self):
print "B"
super(B,self).__init__()
這時候,又有一個問題:當類是多重繼承的時候,super訪問的是哪一個類呢?
super實際上是通過__MRO__序列來確定訪問哪一個類的...實際上就是調用__MRO__中此類後面的一個類的方法.
比如序列為[F,E,D,C,B,A]那麼F中的super就是E,E的就是D
super和按照類名訪問 混合使用帶來的坑
代碼如下:
class A(object):
def __init__(self):
print "enter A"
print "leave A"
class B(object):
def __init__(self):
print "enter B"
print "leave B"
class C(A):
def __init__(self):
print "enter C"
super(C, self).__init__()
print "leave C"
class D(A):
def __init__(self):
print "enter D"
super(D, self).__init__()
print "leave D"
class E(B, C):
def __init__(self):
print "enter E"
B.__init__(self)
C.__init__(self)
print "leave E"
class F(E, D):
def __init__(self):
print "enter F"
E.__init__(self)
D.__init__(self)
print "leave F"
這時候列印出來是:
代碼如下:
enter F
enter E
enter B
leave B
enter C
enter D
enter A
leave A
leave D
leave C
leave E
enter D
enter A
leave A
leave D
leave F
可以看出來D和A的初始化函數被亂入了兩次!
按類名訪問就相當於C語言之前的GOTO語句...亂跳,然後再用super按順序訪問..就有問題了
所以建議就是要麼一直用super,要麼一直用按照類名訪問
最佳實現:
避免多重繼承
super使用一致
不要混用經典類和新式類
調用父類的時候注意檢查類層次
以上便是本人對於python類的繼承的認識了,希望對大家能有所幫助
❻ Python中object has no attribute是什麼問題
沒有繼承Object。
首先什麼是新式類 經典類呢:
#新式類是指繼承object的類
class A(obect):
#經典類是指沒有繼承object的類
class A:
Python中推薦大家使用新式類 1.新的肯定好哈,已經兼容經典類
2.修復了經典類中多繼承出現的bug
下面我們著重說一下多繼承的bug 如圖:
BC 為A的子類, D為BC的子類 ,A中有save方法,C對其進行了重寫
在經典類中 調用D的save方法 搜索按深度優先 路徑B-A-C, 執行的為A中save 顯然不合理
在新式類的 調用D的save方法 搜索按廣度優先 路徑B-C-A, 執行的為C中save
#經典類
class A:
def __init__(self):
print 'this is A'
def save(self):
print 'come from A'
class B(A):
def __init__(self):
print 'this is B'
class C(A):
def __init__(self):
print 'this is C'
def save(self):
print 'come from C'
class D(B,C):
def __init__(self):
print 'this is D'
d1=D()
d1.save() #結果為'come from A
#新式類
class A(object):
def __init__(self):
print 'this is A'
def save(self):
print 'come from A'
class B(A):
def __init__(self):
print 'this is B'
class C(A):
def __init__(self):
print 'this is C'
def save(self):
print 'come from C'
class D(B,C):
def __init__(self):
print 'this is D'
d1=D()
d1.save() #結果為'come from C'