Python入門篇之面向?qū)ο?/h1>
更新時間:2014年10月20日 11:34:42 投稿:hebedich
面向?qū)ο笤O(shè)計(OOD)不會特別要求面向?qū)ο缶幊陶Z言。事實上OOD可以由純結(jié)構(gòu)化語言來實現(xiàn),比如C,但如果想要構(gòu)造具備對象性質(zhì)和特點的數(shù)據(jù)類型,就需要在程序上作更多的努力。當一門語言內(nèi)建OO特性,OO編程開發(fā)就會更加方便高效。
面向?qū)ο笤O(shè)計與面向?qū)ο缶幊痰年P(guān)系
面向?qū)ο笤O(shè)計(OOD)不會特別要求面向?qū)ο缶幊陶Z言。事實上,OOD 可以由純結(jié)構(gòu)化語言來實現(xiàn),比如 C,但如果想要構(gòu)造具備對象性質(zhì)和特點的數(shù)據(jù)類型,就需要在程序上作更多的努力。當一門語言內(nèi)建 OO 特性,OO 編程開發(fā)就會更加方便高效。另一方面,一門面向?qū)ο蟮恼Z言不一定會強制你寫 OO 方面的程序。例如 C++可以被認為“更好的C”;而 Java,則要求萬物皆類,此外還規(guī)定,一個源文件對應一個類定義。然而,在 Python 中,類和 OOP 都不是日常編程所必需的。盡管它從一開始設(shè)計就是面向?qū)ο蟮?,并且結(jié)構(gòu)上支持 OOP,但Python 沒有限定或要求你在你的應用中寫 OO 的代碼。OOP 是一門強大的工具,不管你是準備進入,學習,過渡,或是轉(zhuǎn)向 OOP,都可以任意支配??紤]用 OOD 來工作的一個最重要的原因,在于它直接提供建模和解決現(xiàn)實世界問題和情形的途徑。
類
類是一種數(shù)據(jù)結(jié)構(gòu),我們可以用它來定義對象,后者把數(shù)據(jù)值和行為特性融合在一起。類是現(xiàn)實世界的抽象的實體以編程形式出現(xiàn)。實例是這些對象的具體化??梢灶惐纫幌?,類是藍圖或者模型,用來產(chǎn)生真實的物體(實例)。類還可以派生出相似但有差異的子類。編程中類的概念就應用了很多這樣的特征。在 Python 中,類聲明與函數(shù)聲明很相似,頭一行用一個相應的關(guān)鍵字,接下來是一個作為它的定義的代碼體,如下所示:
復制代碼 代碼如下:
def functionName(args):
'function documentation string' #函數(shù)文檔字符串
function_suite #函數(shù)體
class ClassName(object):
'class documentation string' #類文檔字符串
class_suite #類體
二者都允許你在他們的聲明中創(chuàng)建函數(shù),閉包或者內(nèi)部函數(shù)(即函數(shù)內(nèi)的函數(shù)),還有在類中定義的方法。最大的不同在于你運行函數(shù),而類會創(chuàng)建一個對象。類就像一個 Python 容器類型。盡管類是對象(在 Python 中,一切皆對象),但正被定義時,它們還不是對象的實現(xiàn)。
創(chuàng)建類
Python 類使用 class 關(guān)鍵字來創(chuàng)建。簡單的類的聲明可以是關(guān)鍵字后緊跟類名:
復制代碼 代碼如下:
class ClassName(bases):
'class documentation string' #'類文檔字符串'
class_suite #類體
基類是一個或多個用于繼承的父類的集合;類體由所有聲明語句,類成員定義,數(shù)據(jù)屬性和函數(shù)組成。類通常在一個模塊的頂層進行定義,以便類實例能夠在類所定義
的源代碼文件中的任何地方被創(chuàng)建。
聲明與定義
對于 Python 函數(shù)來說,聲明與定義類沒什么區(qū)別,因為他們是同時進行的,定義(類體)緊跟在聲明(含 class 關(guān)鍵字的頭行[header line])和可選的文檔字符串后面。同時,所有的方法也必須同時被定義。如果對 OOP 很熟悉,請注意 Python 并不支持純虛函數(shù)(像 C++)或者抽象方法(如在 JAVA 中),這些都強制程序員在子類中定義方法。作為替代方法,你可以簡單地在基類方法中引發(fā) NotImplementedError 異常,這樣可以獲得類似的效果。
類屬性
屬性就是屬于另一個對象的數(shù)據(jù)或者函數(shù)元素,可以通過我們熟悉的句點屬性標識法來訪問。一些 Python 類型比如復數(shù)有數(shù)據(jù)屬性(實部和虛部),而另外一些,像列表和字典,擁有方法(函數(shù)屬性)。
有關(guān)屬性的一個有趣的地方是,當你正訪問一個屬性時,它同時也是一個對象,擁有它自己的屬性,可以訪問,這導致了一個屬性鏈,比如,myThing,subThing,subSubThing.等等
類的數(shù)據(jù)屬性
數(shù)據(jù)屬性僅僅是所定義的類的變量。它們可以像任何其它變量一樣在類創(chuàng)建后被使用,并且,要么是由類中的方法來更新,要么是在主程序其它什么地方被更新。
這種屬性已為 OO 程序員所熟悉,即靜態(tài)變量,或者是靜態(tài)數(shù)據(jù)。它們表示這些數(shù)據(jù)是與它們所屬的類對象綁定的,不依賴于任何類實例。如果你是一位 Java 或 C++程序員,這種類型的數(shù)據(jù)相當于在一個變量聲明前加上 static 關(guān)鍵字。靜態(tài)成員通常僅用來跟蹤與類相關(guān)的值。
看下面的例子,使用類數(shù)據(jù)屬性(foo):
復制代碼 代碼如下:
>>> class c(object):
foo = 100
>>> print c.foo
100
>>> c.foo+=1
>>> c.foo
101
方法
復制代碼 代碼如下:
>>> class MyClass(object):
def myNoActionMethod(self):
pass
>>> mc = MyClass()
>>> mc.myNoActionMethod()
任何像函數(shù)一樣對 myNoActionMethod 自身的調(diào)用都將失?。?/p>
復制代碼 代碼如下:
>>> myNoActionMethod() Traceback (innermost last):
File "<stdin>", line 1, in ?
myNoActionMethod() NameError: myNoActionMethod
甚至由類對象調(diào)用此方法也失敗了。
復制代碼 代碼如下:
>>> MyClass.myNoActionMethod() Traceback (innermost last):
File "<stdin>", line 1, in ?
MyClass.myNoActionMethod()
TypeError: unbound method must be called with class
instance 1st argument
綁定(綁定及非綁定方法)
為與 OOP 慣例保持一致,Python 嚴格要求,沒有實例,方法是不能被調(diào)用的。這種限制即 Python所描述的綁定概念(binding),在此,方法必須綁定(到一個實例)才能直接被調(diào)用。非綁定的方法可能可以被調(diào)用,但實例對象一定要明確給出,才能確保調(diào)用成功。然而,不管是否綁定,方法都是它所在的類的固有屬性,即使它們幾乎總是通過實例來調(diào)用的。
決定類的屬性
要知道一個類有哪些屬性,有兩種方法。最簡單的是使用 dir()內(nèi)建函數(shù)。另外是通過訪問類的字典屬性__dict__,這是所有類都具備的特殊屬性之一。
看一下下面的例子:
復制代碼 代碼如下:
>>> class myclass(object):
'myclass class definition' #類定義
myVersion = '1.1' #靜態(tài)數(shù)據(jù)
def showVesion(self): #方法
print myclass.myVersion
>>> dir(myclass)
運行結(jié)果:
復制代碼 代碼如下:
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'myVersion', 'showVesion']
使用:
復制代碼 代碼如下:
>>> myclass.__dict__
dict_proxy({'__module__': '__main__', 'showVesion': <function showVesion at 0x0134C9B0>, '__dict__': <attribute '__dict__' of 'myclass' objects>, 'myVersion': '1.1', '__weakref__': <attribute '__weakref__' of 'myclass' objects>, '__doc__': 'myclass class definition'})
從上面可以看到,dir()返回的僅是對象的屬性的一個名字列表,而__dict__返回的是一個字典,它的鍵(keys)是屬性名,鍵值(values)是相應的屬性對象的數(shù)據(jù)值。
結(jié)果還顯示了 MyClass 類中兩個熟悉的屬性,showMyVersion 和 myVersion,以及一些新的屬性。這些屬性,__doc__及__module__,是所有類都具備的特殊類屬性(另外還有__dict__)。。內(nèi)建的 vars()函數(shù)接受類對象作為參數(shù),返回類的__dict__屬性的內(nèi)容。
特殊的類屬性
對任何類C,表顯示了類C的所有特殊屬性:
C.__name__ 類C的名字(字符串)
C.__doc__ 類C的文檔字符串
C.__bases__ 類C的所有父類構(gòu)成的元組
C.__dict__ 類C的屬性
C.__module__ 類C定義所在的模塊(1.5 版本新增)
C.__class__ 實例C對應的類(僅新式類中)
復制代碼 代碼如下:
>>> myclass.__name__
'myclass'
>>> myclass.__doc__
'myclass class definition'
>>> myclass.__bases__
(<type 'object'>,)
>>> print myclass.__dict__
{'__module__': '__main__', 'showVesion': <function showVesion at 0x0134C9B0>, '__dict__': <attribute '__dict__' of 'myclass' objects>, 'myVersion': '1.1', '__weakref__': <attribute '__weakref__' of 'myclass' objects>, '__doc__': 'myclass class definition'}
>>> myclass.__module__
'__main__'
>>> myclass.__class__
<type 'type'>
實例
如果說類是一種數(shù)據(jù)結(jié)構(gòu)定義類型,那么實例則聲明了一個這種類型的變量。實例是那些主要用在運行期時的對象,類被實例化得到實例,該實例的類型就是這個被實例化的類。
初始化:通過調(diào)用類對象來創(chuàng)建實例
Python 的方式更加簡單。一旦定義了一個類,創(chuàng)建實例比調(diào)用一個函數(shù)還容易------不費吹灰之力。實例化的實現(xiàn),可以使用函數(shù)操作符,如下示:
>>> class MyClass(object): # define class 定義類
pass
>>> mc = MyClass() # instantiate class 初始化類
__init__()"構(gòu)造器"方法
當類被調(diào)用,實例化的第一步是創(chuàng)建實例對象。一旦對象創(chuàng)建了,Python 檢查是否實現(xiàn)了__init__()方法。默認情況下,如果沒有定義(或覆蓋)特殊方法__init__(),對實例不會施加任何特別的操作.任何所需的特定操作,都需要程序員實現(xiàn)__init__(),覆蓋它的默認行為。
如果__init__()沒有實現(xiàn),則返回它的對象,實例化過程完畢。
如果__init__()已經(jīng)被實現(xiàn),那么它將被調(diào)用,實例對象作為第一個參數(shù)(self)被傳遞進去,像標準方法調(diào)用一樣。調(diào)用類時,傳進的任何參數(shù)都交給了__init__()。實際中,你可以想像成這樣:把創(chuàng)建實例的調(diào)用當成是對構(gòu)造器的調(diào)用。
__new__()“構(gòu)造器”方法
與__init__()相比,__new__()方法更像一個真正的構(gòu)造器。需要一種途徑來實例化不可變對象,比如,派生字符串,數(shù)字,等等。在這種情況下,解釋器則調(diào)用類的__new__()方法,一個靜態(tài)方法,并且傳入的參數(shù)是在類實例化操作時生成的。__new__()會調(diào)用父類的__new__()來創(chuàng)建對象(向上代理)。__new__()必須返回一個合法的實例。
__del__()"解構(gòu)器"方法
同樣,有一個相應的特殊解構(gòu)器(destructor)方法名為__del__()。然而,由于 Python 具有垃圾對象回收機制(靠引用計數(shù)),這個函數(shù)要直到該實例對象所有的引用都被清除掉后才會執(zhí)行。Python 中的解構(gòu)器是在實例釋放前提供特殊處理功能的方法,它們通常沒有被實現(xiàn),因為實例很少被顯式釋放。
注意:Python 沒有提供任何內(nèi)部機制來跟蹤一個類有多少個實例被創(chuàng)建了,或者記錄這些實例是些什么東西。如果需要這些功能,你可以顯式加入一些代碼到類定義或者__init__()和__del__()中去。最好的方式是使用一個靜態(tài)成員來記錄實例的個數(shù)。靠保存它們的引用來跟蹤實例對象是很危險的,因為你必須合理管理這些引用,不然,你的引用可能沒辦法釋放(因為還有其它的引用)!看下面一個例子:
復制代碼 代碼如下:
>>> class instCt(object):
count = 0
def __init__(self):
instCt.count += 1
def __del__(self):
instCt.count -= 1
def howMany(self):
return instCt.count
>>> a = instCt()
>>> b = instCt()
>>> b.howMany()
2
>>> a.howMany()
2
>>> del b
>>> a.howMany()
1
>>> del a
>>> instCt.count
0
實例屬性
設(shè)置實例的屬性可以在實例創(chuàng)建后任意時間進行,也可以在能夠訪問實例的代碼中進行。構(gòu)造器__init()__是設(shè)置這些屬性的關(guān)鍵點之一
能夠在“運行時”創(chuàng)建實例屬性,是 Python 類的優(yōu)秀特性之一,Python 不僅是動態(tài)類型,而且在運行時,允許這些對象屬性的動態(tài)創(chuàng)建。這種特性讓人愛不釋
手。當然,創(chuàng)建這樣的屬性時,必須謹慎。一個缺陷是,屬性在條件語句中創(chuàng)建,如果該條件語句塊并未被執(zhí)行,屬性也就不存在,而你在后面的代碼中試著去訪問這些屬性,就會有錯誤發(fā)生。
默認參數(shù)提供默認的實例安裝
在實際應用中,帶默認參數(shù)的__init__()提供一個有效的方式來初始化實例。在很多情況下,默認值表示設(shè)置實例屬性的最常見的情況,如果提供了默認值,我們就沒必要顯式給構(gòu)造器傳值了。
復制代碼 代碼如下:
>> class HotelRoomCalc(object):
'hotel room rate calculate'
def __init__(self, rt, sales = 0.085, rm = 0.1):
'''HotelRoomCalc default arguments:
sales tax == 8.5% and room tax == 10%'''
self.salesTax = sales
self.roomTax = rm
self.roomRate = rt
def calcTotal(self, days = 1):
'Calculate total: default to daily rate'
daily = round((self.roomRate * 14 * (1+self.roomTax + self.salesTax)),2)
return float(days) * daily
>>> sfo = HotelRoomCalc(299)
>>> sfo.calcTotal()
4960.41
>>> sfo.calcTotal(2)
9920.82
>>> sea = HotelRoomCalc(189, 0.086, 0.085)
>>> sea.calcTotal()
3098.47
>>> sea.calcTotal(4)
12393.88
函數(shù)所有的靈活性,比如默認參數(shù),也可以應用到方法中去。在實例化時,可變長度參數(shù)也是一個好的特性
__init__()應當返回 None
采用函數(shù)操作符調(diào)用類對象會創(chuàng)建一個類實例,也就是說這樣一種調(diào)用過程返回的對象就是實例,下面示例可以看出:
復制代碼 代碼如下:
>>> class MyClass(object):
pass
>>> mc = MyClass()
>>> mc
<__main__.MyClass object at 0x0134E610>
如果定義了構(gòu)造器,它不應當返回任何對象,因為實例對象是自動在實例化調(diào)用后返回的。相應地,__init__()就不應當返回任何對象(應當為 None);否則,就可能出現(xiàn)沖突,因為只能返回實例。試著返回非 None 的任何其它對象都會導致 TypeError 異常:
復制代碼 代碼如下:
>>> class MyClass:
def __init__(self):
print 'initialized'
return 1
>>> mc = MyClass()
initialized
Traceback (most recent call last):
File "<pyshell#86>", line 1, in <module>
mc = MyClass()
TypeError: __init__() should return None
查看實例屬性
內(nèi)建函數(shù) dir()可以顯示類屬性,同樣還可以打印所有實例屬性:
復制代碼 代碼如下:
>>> c = C()
>>> c.foo = 'he'
>>> c.bar = 'isa'
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo']
與類相似,實例也有一個__dict__特殊屬性(可以調(diào)用 vars()并傳入一個實例來獲?。?,它是實例屬性構(gòu)成的一個字典:
復制代碼 代碼如下:
>>> c.__dict__
{'foo': 'he', 'bar': 'isa'}
特殊的實例屬性
實例僅有兩個特殊屬性。對于任意對象I:
I.__class__ 實例化 I 的類
I.__dict__ I 的屬性
復制代碼 代碼如下:
>>> class C(object):
pass
>>> c = C()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
>>> c.__dict__
{}
>>> c.__class__
<class '__main__.C'>>>> #可以看到,c還沒有屬性
>>> c.foo = 1
>>> c.bar = 'ewe'
>>> '%d can of %s please' % (c.foo, c.bar)
'1 can of ewe please'
>>> c.__dict__
{'foo': 1, 'bar': 'ewe'}
內(nèi)建類型屬性
內(nèi)建類型也是類,對內(nèi)建類型也可以使用dir(),與任何其它對象一樣,可以得到一個包含它屬性名字的列表:
復制代碼 代碼如下:
>>> x = 2 + 2.4j
>>> x.__class__
<type 'complex'>
>>> dir(x)
['__abs__', '__add__', '__class__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__eq__', '__float__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__int__', '__le__', '__long__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__nonzero__', '__pos__', '__pow__', '__radd__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rmod__', '__rmul__', '__rpow__', '__rsub__', '__rtruediv__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', 'conjugate', 'imag', 'real']
試著訪問__dict__會失敗,因為在內(nèi)建類型中,不存在這個屬性
實例屬性 vs 類屬性
類屬性僅是與類相關(guān)的數(shù)據(jù)值,和實例屬性不同,類屬性和實例無關(guān)。這些值像靜態(tài)成員那樣被引用,即使在多次實例化中調(diào)用類,它們的值都保持不變。不管如何,靜態(tài)成員不會因為實例而改變它們的值,除非實例中顯式改變它們的值。類和實例都是名字空間。類是類屬性的名字空間,實例則是實例屬性的。
關(guān)于類屬性和實例屬性,還有一些方面需要指出??刹捎妙悂碓L問類屬性,如果實例沒有同名的屬性的話,你也可以用實例來訪問。
訪問類屬性
類屬性可通過類或?qū)嵗齺碓L問。下面的示例中,類 C 在創(chuàng)建時,帶一個 version 屬性,這樣通過類對象來訪問它是很自然的了,比如,C.version
復制代碼 代碼如下:
>>> class C(object):
version = 2
>>> c = C()
>>> C.version
2
>>> c.version
2
>>> C.version += 2
>>> C.version
4
>>> c.version
4
從實例中訪問類屬性須謹慎
與通常 Python 變量一樣,任何對實例屬性的賦值都會創(chuàng)建一個實例屬性(如果不存在的話)并且對其賦值。如果類屬性中存在同名的屬性,副作用即產(chǎn)生。
復制代碼 代碼如下:
>>> class Foo(object):
x = 1
>>> foo =Foo()
>>> foo.x
1
>>> foo.x = 2
>>> Foo.x
1
使用del后
復制代碼 代碼如下:
>>> del foo.x
>>> foo.x
1
靜態(tài)成員,如其名所言,任憑整個實例(及其屬性)的如何進展,它都不理不采(因此獨立于實例)。同時,當一個實例在類屬性被修改后才創(chuàng)建,那么更新的值就將生效。類屬性的修改會影響到所有的實例:
復制代碼 代碼如下:
>>> class C(object):
spam = 11
>>> c1 = C()
>>> c1.spam
11
>>> C.spam += 2
>>> C.spam
13
>>> c1.spam
13
>>> c2 = C()
>>> c2.spam
13
>>> del c1
>>> C.spam += 3
>>> c2.spam
16
正如上面所看到的那樣,使用實例屬性來試著修改類屬性是很危險的。原因在于實例擁有它們自已的屬性集,在 Python 中沒有明確的方法來指示你想要修改同名的類屬性,修改類屬性需要使用類名,而不是實例名。
靜態(tài)方法和類方法
靜態(tài)方法和類方法在 Python2.2 中引入。經(jīng)典類及新式(new-style)類中都可以使用它。一對內(nèi)建函數(shù)被引入,用于將作為類定義的一部分的某一方法聲明“標記”(tag),“強制類型轉(zhuǎn)換”(cast)或者“轉(zhuǎn)換”(convert)為這兩種類型的方法之一。
現(xiàn)在讓我們看一下在經(jīng)典類中創(chuàng)建靜態(tài)方法和類方法的一些例子:
復制代碼 代碼如下:
>>> class TestStaticMethod:
def foo():
print 'calling static method foo()'
foo = staticmethod(foo)
>>> class TestClassMethod:
def foo(cls):
print 'calling class method foo()'
print 'foo() is part of class:', cls.__name__
foo = classmethod(foo)
對應的內(nèi)建函數(shù)被轉(zhuǎn)換成它們相應的類型,并且重新賦值給了相同的變量名。如果沒有調(diào)用這兩個函數(shù),二者都會在 Python 編譯器中產(chǎn)生錯誤,顯示需要帶 self 的常規(guī)方法聲明。
復制代碼 代碼如下:
>>> tsm = TestStaticMethod()
>>> TestStaticMethod.foo()
calling static method foo()
>>> tsm.foo()
calling static method foo()
>>> tcm = TestClassMethod()
>>> TestClassMethod.foo()
calling class method foo()
foo() is part of class: TestClassMethod
>>> tcm.foo()
calling class method foo()
foo() is part of class: TestClassMethod
使用函數(shù)修飾符:
在 Python2.4 中加入的新特征。你可以用它把一個函數(shù)應用到另個函數(shù)對象上, 而且新函數(shù)對象依然綁定在原來的變量。我們正是需要它來整理語法。通過使用 decorators,我們可以避免像上面那樣的重新賦值:
復制代碼 代碼如下:
>>> class TestStaticMethod:
@staticmethod
def foo():
print 'calling static method foo()'
>>> class TestClassMethod:
@classmethod
def foo(cls):
print 'calling class method foo()'
print 'foo() is part of class:', cls.__name__
相關(guān)文章
-
python網(wǎng)絡編程調(diào)用recv函數(shù)完整接收數(shù)據(jù)的三種方法
本文主要介紹了python網(wǎng)絡編程調(diào)用recv函數(shù)完整接收數(shù)據(jù)的三種方法。具有很好的參考價值,下面跟著小編一起來看下吧 2017-03-03
-
Pytorch中torch.argmax()函數(shù)使用及說明
這篇文章主要介紹了Pytorch中torch.argmax()函數(shù)使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教 2023-01-01
-
利用Pyhton中的requests包進行網(wǎng)頁訪問測試的方法
今天小編就為大家分享一篇利用Pyhton中的requests包進行網(wǎng)頁訪問測試的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 2018-12-12
-
使用Python的Django框架結(jié)合jQuery實現(xiàn)AJAX購物車頁面
這篇文章主要介紹了使用Python的Django框架結(jié)合jQuery實現(xiàn)AJAX購物車頁面的方法,示例基于Django中構(gòu)建好的JSON格式的RESTful API需要的朋友可以參考下 2016-04-04
最新評論
面向?qū)ο笤O(shè)計與面向?qū)ο缶幊痰年P(guān)系
面向?qū)ο笤O(shè)計(OOD)不會特別要求面向?qū)ο缶幊陶Z言。事實上,OOD 可以由純結(jié)構(gòu)化語言來實現(xiàn),比如 C,但如果想要構(gòu)造具備對象性質(zhì)和特點的數(shù)據(jù)類型,就需要在程序上作更多的努力。當一門語言內(nèi)建 OO 特性,OO 編程開發(fā)就會更加方便高效。另一方面,一門面向?qū)ο蟮恼Z言不一定會強制你寫 OO 方面的程序。例如 C++可以被認為“更好的C”;而 Java,則要求萬物皆類,此外還規(guī)定,一個源文件對應一個類定義。然而,在 Python 中,類和 OOP 都不是日常編程所必需的。盡管它從一開始設(shè)計就是面向?qū)ο蟮?,并且結(jié)構(gòu)上支持 OOP,但Python 沒有限定或要求你在你的應用中寫 OO 的代碼。OOP 是一門強大的工具,不管你是準備進入,學習,過渡,或是轉(zhuǎn)向 OOP,都可以任意支配??紤]用 OOD 來工作的一個最重要的原因,在于它直接提供建模和解決現(xiàn)實世界問題和情形的途徑。
類
類是一種數(shù)據(jù)結(jié)構(gòu),我們可以用它來定義對象,后者把數(shù)據(jù)值和行為特性融合在一起。類是現(xiàn)實世界的抽象的實體以編程形式出現(xiàn)。實例是這些對象的具體化??梢灶惐纫幌?,類是藍圖或者模型,用來產(chǎn)生真實的物體(實例)。類還可以派生出相似但有差異的子類。編程中類的概念就應用了很多這樣的特征。在 Python 中,類聲明與函數(shù)聲明很相似,頭一行用一個相應的關(guān)鍵字,接下來是一個作為它的定義的代碼體,如下所示:
def functionName(args):
'function documentation string' #函數(shù)文檔字符串
function_suite #函數(shù)體
class ClassName(object):
'class documentation string' #類文檔字符串
class_suite #類體
二者都允許你在他們的聲明中創(chuàng)建函數(shù),閉包或者內(nèi)部函數(shù)(即函數(shù)內(nèi)的函數(shù)),還有在類中定義的方法。最大的不同在于你運行函數(shù),而類會創(chuàng)建一個對象。類就像一個 Python 容器類型。盡管類是對象(在 Python 中,一切皆對象),但正被定義時,它們還不是對象的實現(xiàn)。
創(chuàng)建類
Python 類使用 class 關(guān)鍵字來創(chuàng)建。簡單的類的聲明可以是關(guān)鍵字后緊跟類名:
class ClassName(bases):
'class documentation string' #'類文檔字符串'
class_suite #類體
基類是一個或多個用于繼承的父類的集合;類體由所有聲明語句,類成員定義,數(shù)據(jù)屬性和函數(shù)組成。類通常在一個模塊的頂層進行定義,以便類實例能夠在類所定義
的源代碼文件中的任何地方被創(chuàng)建。
聲明與定義
對于 Python 函數(shù)來說,聲明與定義類沒什么區(qū)別,因為他們是同時進行的,定義(類體)緊跟在聲明(含 class 關(guān)鍵字的頭行[header line])和可選的文檔字符串后面。同時,所有的方法也必須同時被定義。如果對 OOP 很熟悉,請注意 Python 并不支持純虛函數(shù)(像 C++)或者抽象方法(如在 JAVA 中),這些都強制程序員在子類中定義方法。作為替代方法,你可以簡單地在基類方法中引發(fā) NotImplementedError 異常,這樣可以獲得類似的效果。
類屬性
屬性就是屬于另一個對象的數(shù)據(jù)或者函數(shù)元素,可以通過我們熟悉的句點屬性標識法來訪問。一些 Python 類型比如復數(shù)有數(shù)據(jù)屬性(實部和虛部),而另外一些,像列表和字典,擁有方法(函數(shù)屬性)。
有關(guān)屬性的一個有趣的地方是,當你正訪問一個屬性時,它同時也是一個對象,擁有它自己的屬性,可以訪問,這導致了一個屬性鏈,比如,myThing,subThing,subSubThing.等等
類的數(shù)據(jù)屬性
數(shù)據(jù)屬性僅僅是所定義的類的變量。它們可以像任何其它變量一樣在類創(chuàng)建后被使用,并且,要么是由類中的方法來更新,要么是在主程序其它什么地方被更新。
這種屬性已為 OO 程序員所熟悉,即靜態(tài)變量,或者是靜態(tài)數(shù)據(jù)。它們表示這些數(shù)據(jù)是與它們所屬的類對象綁定的,不依賴于任何類實例。如果你是一位 Java 或 C++程序員,這種類型的數(shù)據(jù)相當于在一個變量聲明前加上 static 關(guān)鍵字。靜態(tài)成員通常僅用來跟蹤與類相關(guān)的值。
看下面的例子,使用類數(shù)據(jù)屬性(foo):
>>> class c(object):
foo = 100
>>> print c.foo
100
>>> c.foo+=1
>>> c.foo
101
方法
>>> class MyClass(object):
def myNoActionMethod(self):
pass
>>> mc = MyClass()
>>> mc.myNoActionMethod()
任何像函數(shù)一樣對 myNoActionMethod 自身的調(diào)用都將失?。?/p>
>>> myNoActionMethod() Traceback (innermost last):
File "<stdin>", line 1, in ?
myNoActionMethod() NameError: myNoActionMethod
甚至由類對象調(diào)用此方法也失敗了。
>>> MyClass.myNoActionMethod() Traceback (innermost last):
File "<stdin>", line 1, in ?
MyClass.myNoActionMethod()
TypeError: unbound method must be called with class
instance 1st argument
綁定(綁定及非綁定方法)
為與 OOP 慣例保持一致,Python 嚴格要求,沒有實例,方法是不能被調(diào)用的。這種限制即 Python所描述的綁定概念(binding),在此,方法必須綁定(到一個實例)才能直接被調(diào)用。非綁定的方法可能可以被調(diào)用,但實例對象一定要明確給出,才能確保調(diào)用成功。然而,不管是否綁定,方法都是它所在的類的固有屬性,即使它們幾乎總是通過實例來調(diào)用的。
決定類的屬性
要知道一個類有哪些屬性,有兩種方法。最簡單的是使用 dir()內(nèi)建函數(shù)。另外是通過訪問類的字典屬性__dict__,這是所有類都具備的特殊屬性之一。
看一下下面的例子:
>>> class myclass(object):
'myclass class definition' #類定義
myVersion = '1.1' #靜態(tài)數(shù)據(jù)
def showVesion(self): #方法
print myclass.myVersion
>>> dir(myclass)
運行結(jié)果:
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'myVersion', 'showVesion']
使用:
>>> myclass.__dict__
dict_proxy({'__module__': '__main__', 'showVesion': <function showVesion at 0x0134C9B0>, '__dict__': <attribute '__dict__' of 'myclass' objects>, 'myVersion': '1.1', '__weakref__': <attribute '__weakref__' of 'myclass' objects>, '__doc__': 'myclass class definition'})
從上面可以看到,dir()返回的僅是對象的屬性的一個名字列表,而__dict__返回的是一個字典,它的鍵(keys)是屬性名,鍵值(values)是相應的屬性對象的數(shù)據(jù)值。
結(jié)果還顯示了 MyClass 類中兩個熟悉的屬性,showMyVersion 和 myVersion,以及一些新的屬性。這些屬性,__doc__及__module__,是所有類都具備的特殊類屬性(另外還有__dict__)。。內(nèi)建的 vars()函數(shù)接受類對象作為參數(shù),返回類的__dict__屬性的內(nèi)容。
特殊的類屬性
對任何類C,表顯示了類C的所有特殊屬性:
C.__name__ 類C的名字(字符串)
C.__doc__ 類C的文檔字符串
C.__bases__ 類C的所有父類構(gòu)成的元組
C.__dict__ 類C的屬性
C.__module__ 類C定義所在的模塊(1.5 版本新增)
C.__class__ 實例C對應的類(僅新式類中)
>>> myclass.__name__
'myclass'
>>> myclass.__doc__
'myclass class definition'
>>> myclass.__bases__
(<type 'object'>,)
>>> print myclass.__dict__
{'__module__': '__main__', 'showVesion': <function showVesion at 0x0134C9B0>, '__dict__': <attribute '__dict__' of 'myclass' objects>, 'myVersion': '1.1', '__weakref__': <attribute '__weakref__' of 'myclass' objects>, '__doc__': 'myclass class definition'}
>>> myclass.__module__
'__main__'
>>> myclass.__class__
<type 'type'>
實例
如果說類是一種數(shù)據(jù)結(jié)構(gòu)定義類型,那么實例則聲明了一個這種類型的變量。實例是那些主要用在運行期時的對象,類被實例化得到實例,該實例的類型就是這個被實例化的類。
初始化:通過調(diào)用類對象來創(chuàng)建實例
Python 的方式更加簡單。一旦定義了一個類,創(chuàng)建實例比調(diào)用一個函數(shù)還容易------不費吹灰之力。實例化的實現(xiàn),可以使用函數(shù)操作符,如下示:
>>> class MyClass(object): # define class 定義類
pass
>>> mc = MyClass() # instantiate class 初始化類
__init__()"構(gòu)造器"方法
當類被調(diào)用,實例化的第一步是創(chuàng)建實例對象。一旦對象創(chuàng)建了,Python 檢查是否實現(xiàn)了__init__()方法。默認情況下,如果沒有定義(或覆蓋)特殊方法__init__(),對實例不會施加任何特別的操作.任何所需的特定操作,都需要程序員實現(xiàn)__init__(),覆蓋它的默認行為。
如果__init__()沒有實現(xiàn),則返回它的對象,實例化過程完畢。
如果__init__()已經(jīng)被實現(xiàn),那么它將被調(diào)用,實例對象作為第一個參數(shù)(self)被傳遞進去,像標準方法調(diào)用一樣。調(diào)用類時,傳進的任何參數(shù)都交給了__init__()。實際中,你可以想像成這樣:把創(chuàng)建實例的調(diào)用當成是對構(gòu)造器的調(diào)用。
__new__()“構(gòu)造器”方法
與__init__()相比,__new__()方法更像一個真正的構(gòu)造器。需要一種途徑來實例化不可變對象,比如,派生字符串,數(shù)字,等等。在這種情況下,解釋器則調(diào)用類的__new__()方法,一個靜態(tài)方法,并且傳入的參數(shù)是在類實例化操作時生成的。__new__()會調(diào)用父類的__new__()來創(chuàng)建對象(向上代理)。__new__()必須返回一個合法的實例。
__del__()"解構(gòu)器"方法
同樣,有一個相應的特殊解構(gòu)器(destructor)方法名為__del__()。然而,由于 Python 具有垃圾對象回收機制(靠引用計數(shù)),這個函數(shù)要直到該實例對象所有的引用都被清除掉后才會執(zhí)行。Python 中的解構(gòu)器是在實例釋放前提供特殊處理功能的方法,它們通常沒有被實現(xiàn),因為實例很少被顯式釋放。
注意:Python 沒有提供任何內(nèi)部機制來跟蹤一個類有多少個實例被創(chuàng)建了,或者記錄這些實例是些什么東西。如果需要這些功能,你可以顯式加入一些代碼到類定義或者__init__()和__del__()中去。最好的方式是使用一個靜態(tài)成員來記錄實例的個數(shù)。靠保存它們的引用來跟蹤實例對象是很危險的,因為你必須合理管理這些引用,不然,你的引用可能沒辦法釋放(因為還有其它的引用)!看下面一個例子:
>>> class instCt(object):
count = 0
def __init__(self):
instCt.count += 1
def __del__(self):
instCt.count -= 1
def howMany(self):
return instCt.count
>>> a = instCt()
>>> b = instCt()
>>> b.howMany()
2
>>> a.howMany()
2
>>> del b
>>> a.howMany()
1
>>> del a
>>> instCt.count
0
實例屬性
設(shè)置實例的屬性可以在實例創(chuàng)建后任意時間進行,也可以在能夠訪問實例的代碼中進行。構(gòu)造器__init()__是設(shè)置這些屬性的關(guān)鍵點之一
能夠在“運行時”創(chuàng)建實例屬性,是 Python 類的優(yōu)秀特性之一,Python 不僅是動態(tài)類型,而且在運行時,允許這些對象屬性的動態(tài)創(chuàng)建。這種特性讓人愛不釋
手。當然,創(chuàng)建這樣的屬性時,必須謹慎。一個缺陷是,屬性在條件語句中創(chuàng)建,如果該條件語句塊并未被執(zhí)行,屬性也就不存在,而你在后面的代碼中試著去訪問這些屬性,就會有錯誤發(fā)生。
默認參數(shù)提供默認的實例安裝
在實際應用中,帶默認參數(shù)的__init__()提供一個有效的方式來初始化實例。在很多情況下,默認值表示設(shè)置實例屬性的最常見的情況,如果提供了默認值,我們就沒必要顯式給構(gòu)造器傳值了。
>> class HotelRoomCalc(object):
'hotel room rate calculate'
def __init__(self, rt, sales = 0.085, rm = 0.1):
'''HotelRoomCalc default arguments:
sales tax == 8.5% and room tax == 10%'''
self.salesTax = sales
self.roomTax = rm
self.roomRate = rt
def calcTotal(self, days = 1):
'Calculate total: default to daily rate'
daily = round((self.roomRate * 14 * (1+self.roomTax + self.salesTax)),2)
return float(days) * daily
>>> sfo = HotelRoomCalc(299)
>>> sfo.calcTotal()
4960.41
>>> sfo.calcTotal(2)
9920.82
>>> sea = HotelRoomCalc(189, 0.086, 0.085)
>>> sea.calcTotal()
3098.47
>>> sea.calcTotal(4)
12393.88
函數(shù)所有的靈活性,比如默認參數(shù),也可以應用到方法中去。在實例化時,可變長度參數(shù)也是一個好的特性
__init__()應當返回 None
采用函數(shù)操作符調(diào)用類對象會創(chuàng)建一個類實例,也就是說這樣一種調(diào)用過程返回的對象就是實例,下面示例可以看出:
>>> class MyClass(object):
pass
>>> mc = MyClass()
>>> mc
<__main__.MyClass object at 0x0134E610>
如果定義了構(gòu)造器,它不應當返回任何對象,因為實例對象是自動在實例化調(diào)用后返回的。相應地,__init__()就不應當返回任何對象(應當為 None);否則,就可能出現(xiàn)沖突,因為只能返回實例。試著返回非 None 的任何其它對象都會導致 TypeError 異常:
>>> class MyClass:
def __init__(self):
print 'initialized'
return 1
>>> mc = MyClass()
initialized
Traceback (most recent call last):
File "<pyshell#86>", line 1, in <module>
mc = MyClass()
TypeError: __init__() should return None
查看實例屬性
內(nèi)建函數(shù) dir()可以顯示類屬性,同樣還可以打印所有實例屬性:
>>> c = C()
>>> c.foo = 'he'
>>> c.bar = 'isa'
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo']
與類相似,實例也有一個__dict__特殊屬性(可以調(diào)用 vars()并傳入一個實例來獲?。?,它是實例屬性構(gòu)成的一個字典:
>>> c.__dict__
{'foo': 'he', 'bar': 'isa'}
特殊的實例屬性
實例僅有兩個特殊屬性。對于任意對象I:
I.__class__ 實例化 I 的類
I.__dict__ I 的屬性
>>> class C(object):
pass
>>> c = C()
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
>>> c.__dict__
{}
>>> c.__class__
<class '__main__.C'>>>> #可以看到,c還沒有屬性
>>> c.foo = 1
>>> c.bar = 'ewe'
>>> '%d can of %s please' % (c.foo, c.bar)
'1 can of ewe please'
>>> c.__dict__
{'foo': 1, 'bar': 'ewe'}
內(nèi)建類型屬性
內(nèi)建類型也是類,對內(nèi)建類型也可以使用dir(),與任何其它對象一樣,可以得到一個包含它屬性名字的列表:
>>> x = 2 + 2.4j
>>> x.__class__
<type 'complex'>
>>> dir(x)
['__abs__', '__add__', '__class__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__eq__', '__float__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__int__', '__le__', '__long__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__nonzero__', '__pos__', '__pow__', '__radd__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rmod__', '__rmul__', '__rpow__', '__rsub__', '__rtruediv__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', 'conjugate', 'imag', 'real']
試著訪問__dict__會失敗,因為在內(nèi)建類型中,不存在這個屬性
實例屬性 vs 類屬性
類屬性僅是與類相關(guān)的數(shù)據(jù)值,和實例屬性不同,類屬性和實例無關(guān)。這些值像靜態(tài)成員那樣被引用,即使在多次實例化中調(diào)用類,它們的值都保持不變。不管如何,靜態(tài)成員不會因為實例而改變它們的值,除非實例中顯式改變它們的值。類和實例都是名字空間。類是類屬性的名字空間,實例則是實例屬性的。
關(guān)于類屬性和實例屬性,還有一些方面需要指出??刹捎妙悂碓L問類屬性,如果實例沒有同名的屬性的話,你也可以用實例來訪問。
訪問類屬性
類屬性可通過類或?qū)嵗齺碓L問。下面的示例中,類 C 在創(chuàng)建時,帶一個 version 屬性,這樣通過類對象來訪問它是很自然的了,比如,C.version
>>> class C(object):
version = 2
>>> c = C()
>>> C.version
2
>>> c.version
2
>>> C.version += 2
>>> C.version
4
>>> c.version
4
從實例中訪問類屬性須謹慎
與通常 Python 變量一樣,任何對實例屬性的賦值都會創(chuàng)建一個實例屬性(如果不存在的話)并且對其賦值。如果類屬性中存在同名的屬性,副作用即產(chǎn)生。
>>> class Foo(object):
x = 1
>>> foo =Foo()
>>> foo.x
1
>>> foo.x = 2
>>> Foo.x
1
使用del后
>>> del foo.x
>>> foo.x
1
靜態(tài)成員,如其名所言,任憑整個實例(及其屬性)的如何進展,它都不理不采(因此獨立于實例)。同時,當一個實例在類屬性被修改后才創(chuàng)建,那么更新的值就將生效。類屬性的修改會影響到所有的實例:
>>> class C(object):
spam = 11
>>> c1 = C()
>>> c1.spam
11
>>> C.spam += 2
>>> C.spam
13
>>> c1.spam
13
>>> c2 = C()
>>> c2.spam
13
>>> del c1
>>> C.spam += 3
>>> c2.spam
16
正如上面所看到的那樣,使用實例屬性來試著修改類屬性是很危險的。原因在于實例擁有它們自已的屬性集,在 Python 中沒有明確的方法來指示你想要修改同名的類屬性,修改類屬性需要使用類名,而不是實例名。
靜態(tài)方法和類方法
靜態(tài)方法和類方法在 Python2.2 中引入。經(jīng)典類及新式(new-style)類中都可以使用它。一對內(nèi)建函數(shù)被引入,用于將作為類定義的一部分的某一方法聲明“標記”(tag),“強制類型轉(zhuǎn)換”(cast)或者“轉(zhuǎn)換”(convert)為這兩種類型的方法之一。
現(xiàn)在讓我們看一下在經(jīng)典類中創(chuàng)建靜態(tài)方法和類方法的一些例子:
>>> class TestStaticMethod:
def foo():
print 'calling static method foo()'
foo = staticmethod(foo)
>>> class TestClassMethod:
def foo(cls):
print 'calling class method foo()'
print 'foo() is part of class:', cls.__name__
foo = classmethod(foo)
對應的內(nèi)建函數(shù)被轉(zhuǎn)換成它們相應的類型,并且重新賦值給了相同的變量名。如果沒有調(diào)用這兩個函數(shù),二者都會在 Python 編譯器中產(chǎn)生錯誤,顯示需要帶 self 的常規(guī)方法聲明。
>>> tsm = TestStaticMethod()
>>> TestStaticMethod.foo()
calling static method foo()
>>> tsm.foo()
calling static method foo()
>>> tcm = TestClassMethod()
>>> TestClassMethod.foo()
calling class method foo()
foo() is part of class: TestClassMethod
>>> tcm.foo()
calling class method foo()
foo() is part of class: TestClassMethod
使用函數(shù)修飾符:
在 Python2.4 中加入的新特征。你可以用它把一個函數(shù)應用到另個函數(shù)對象上, 而且新函數(shù)對象依然綁定在原來的變量。我們正是需要它來整理語法。通過使用 decorators,我們可以避免像上面那樣的重新賦值:
>>> class TestStaticMethod:
@staticmethod
def foo():
print 'calling static method foo()'
>>> class TestClassMethod:
@classmethod
def foo(cls):
print 'calling class method foo()'
print 'foo() is part of class:', cls.__name__
相關(guān)文章
python網(wǎng)絡編程調(diào)用recv函數(shù)完整接收數(shù)據(jù)的三種方法
本文主要介紹了python網(wǎng)絡編程調(diào)用recv函數(shù)完整接收數(shù)據(jù)的三種方法。具有很好的參考價值,下面跟著小編一起來看下吧2017-03-03Pytorch中torch.argmax()函數(shù)使用及說明
這篇文章主要介紹了Pytorch中torch.argmax()函數(shù)使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01利用Pyhton中的requests包進行網(wǎng)頁訪問測試的方法
今天小編就為大家分享一篇利用Pyhton中的requests包進行網(wǎng)頁訪問測試的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-12-12使用Python的Django框架結(jié)合jQuery實現(xiàn)AJAX購物車頁面
這篇文章主要介紹了使用Python的Django框架結(jié)合jQuery實現(xiàn)AJAX購物車頁面的方法,示例基于Django中構(gòu)建好的JSON格式的RESTful API需要的朋友可以參考下2016-04-04