1. 程式人生 > >Python -面向對象(一 基本概念)

Python -面向對象(一 基本概念)

多態 adding 提示 csdn bject key 析構函數 不可移植 一次

一 Python簡單介紹

Python是一個可移植的面向對象的腳本語言。

Python盡管是一個腳本語言,但也是一個全然面向對象的語言。

由於它設計之初把易用性做為很重要的一個考量標準,所以用起來很簡潔,優美(語法很靈活)。所以使用Python能夠高速地編寫出可執行代碼。與C/C++相比。Python程序的執行速度比較慢,一門語言既然能夠生存下來。就有它自己的原因,Python語言也一樣。

當今的計算機處理速度已經很快。對某些任務來說。執行速度並非首要考慮的因素。比方說為了實現數據庫訪問的自己主動化。須要做一個數據庫訪問的代碼生成器,這是一個常見的任務,當中涉及編程的很多方面,包含字符串處理、數據庫訪問,可能還包含一個GUI系統。這個任務顯然不太適合用C或者C++來編寫,由於使用C/C++來開發盡管能夠節省那麽一點執行時間。但卻浪費了大量的開發時間。

所以,語言沒有好不好之分,僅僅有適合不適合之分。

C++是靜態強類型語言,而Python是動態強類型語言。因為是動態語言,所以變量的類型不是用keyword顯式指定,而是在執行時依據賦給它的值動態推斷出來的。

另外,Python也跟C++一樣同一時候支持結構化編程和面向對象編程兩種範式。 Python的特定總結例如以下:

1. Python是面向對象的。它支持繼承以及多重繼承、多態、操作符重載等面向對象的概念。

2. Python是可移植的。

使用Python語言編寫的程序通常無需改動就能夠執行在多個平臺上,這點與Java類似。

可是Python也提供一些Plantform dependent的模塊,使用這些模塊的時候必須小心,由於他們是不可移植的。

3. Python是解釋性語言。準確地說。Python先把源程序編譯成中間代碼(類似於java和c#),然後解釋運行。

但要註意的是,編譯過程對程序猿來說是透明的(這又不同於java和c#)。

4.Python是一門動態語言。它支持元數據編程;支持執行時編譯、執行等現代語言的高級特征。這些都非常難甚至無法在C/C++中實現。Python甚至同意你在執行的時候添加一個對象的成員!

5. 一切都是對象!Python中對象的概念比其它語言豐富得多,比方類是一個對象,因此你能夠在執行的時候使用它。而不像C++一樣,類是編譯時的對象。正由於這樣的原因,使得Python是高度動態的。

6. 自己主動內存管理。像java一樣,你無需管理你的代碼所使用的內存。在C和C++的程序中,那是一場惡夢。

7. 其它:Python能夠使用c/c++的編寫的組件,也能夠把python代碼嵌入到c/c++代碼中運行。

二 類定義

首先看以下的樣例
class ZZClass:
classNum = 0
def __init__(self):
self.num = 1
ZZClass.classNum += 1
print ("ZZClass _init__ called.")


def __del__(self):
ZZClass.classNum -= 1;
print ("ZZClass __del__ called.")

def Hello(self):
print("hello world!")
self.PrintClassNum(10) #普通函數中能夠調用靜態函數


def setNum(self,num):
self.num = num

def getNum(self):
return self.num

@staticmethod
def PrintClassNum(num=100):
print (ZZClass.classNum) #在靜態方法中僅僅能通過類名訪問類變量
#print classNum #在靜態方法中不能直接訪問類變量
#print self.num #
在靜態方法中不能訪問實例變量
print num


@classmethod
def ClassMethod(cls):
#print cls.num #在類方法中不能直接訪問實例變量
print "class method."
print cls.classNum #在類方法中能夠直接訪問類變量


myObj = ZZClass()
myObj.Hello()
ZZClass.PrintClassNum(10) #能夠通過類名來訪問靜態方法
myObj02 = ZZClass()
myObj02.PrintClassNum() #能夠通過對象實例來訪問靜態方法
print myObj.classNum #能夠通過對象實例來訪問類變量
print ZZClass.classNum #能夠通過類名來訪問靜態變量




myObj.setNum(10)
myObj02.setNum(20)
print myObj.getNum()
print myObj02.getNum()

myObj02 = 0
print ZZClass.PrintClassNum()

print ZZClass.ClassMethod() #通過類調用類方法
print myObj.ClassMethod() #通過實例調用類方法



輸出結果:
ZZClass _init__ called. hello world! 1 10 1 10 ZZClass _init__ called. 2 100 2 2 10 20 ZZClass __del__ called. 1 100 None class method. 1 None ZZClass __del__ called.


分析: 1.與C++相比,Python中定義方法採用defkeyword,在類中定義的方法至少會有一個參數,一般以名為‘self‘的變量作為該參數(用其它名稱也能夠),並且須要作為第一個參數。用於對對象的變量引用。類似c++的this指針,僅僅只是在C++中this 指針是隱藏的,由編譯器來處理。 2.函數調用和變量的訪問使用"."而不是"->"訪問。

3._init__類似於構造函數在生成對象時調用,能夠用來進行一些初始化操作,不須要顯示去調用,系統會默認去運行。

構造方法支持重載,假設用戶自己沒有又一次定義構造方法,系統就自己主動運行默認的構造方法。

__del__類似於析構函數在釋放對象時調用,支持重載,能夠在裏面進行一些釋放資源的操作,不須要顯示調用。 4.實例變量和類變量:num是實例變量。classNum是類變量。前者類似C++的成員變量。後者類似C++的類的靜態變量。

類變量是全部對象共享的,不須要對象。能夠直接用“類名.變量名”訪問,與C++一樣,使用對象實例也能夠訪問類變量。實例變量是每一個對象一個拷貝,僅僅能通過對象訪問,實例變量是不須要在類中顯示定義的

註意:類變量能夠通過類名和實例對象兩種方式進行訪問。通過類名改動類變量,該類和全部實例所共享的數據將被改動,再次通過類或實例訪問得到的將是新的數據,這一點與C++是一致的。可是通過實例對象改動類變量,其效果將只作用在該實例上。再次通過類或其他實例訪問得到的仍然是舊的數據。

但這一改動方式將對該類變量實例化,其結果是該實例將得到一個單獨的該變量拷貝,此後此對象不再與類共享該名稱的變量。(這一點Python有點復雜)。看以下的樣例:


class classA:
classVar = ‘‘
def __init__(self):
pass

def set_var1(self,x):
classA.classVar= x

def set_var2(self,y):
self.var2 = y
return self.var2


oa = classA()
ob = classA()


oa.set_var1("class variable.")
print oa.classVar #class variable.
print ob.classVar #class variable.

oa.classVar = "changed."
print oa.classVar #changed.
print ob.classVar #class variable.

oa.set_var1("class variable01")
print oa.classVar #changed.
print ob.classVar #class variable01


ob.set_var1("class variable02")
print oa.classVar #changed.
print ob.classVar #class variable02

ob.set_var2("inst variable")
print ob.var2
#print oa.var2 #error! because var2 is a instance variable

   假設須要在類外改動類屬性,必須通過類對象去引用然後進行改動。假設通過實例對象去引用。會產生一個同名的實例屬性,這樣的方式改動的是實例屬性。不會影響到類屬性。而且之後假設通過實例對象去引用該名稱的屬性。實例屬性會強制屏蔽掉類屬性。即引用的是實例屬性,除非刪除了該實例屬性。
5.普通函數和靜態函數:Hello是普通函數。PrintClassNum是靜態函數。printClassCount()類似C++的靜態函數。就像靜態函數沒有this指針一樣。它也沒有self變量。

靜態函數僅僅能訪問靜態成員變量(類變量)。與C++一樣,能夠使用實例對象和類名來調用靜態函數,僅僅能使用實例變量來調用普通函數。

在類外普通函數(實例函數)僅僅能通過實例對象去調用,不能通過其它方式去調用。
6.類函數和靜態函數 ClassMethod是一個類方法,擁有一個參數(類對象)。

與靜態方法使用類似。他們都能夠通過類和實例進行調用,都無法訪問實例成員。

類方法能夠通過自己的參數cls訪問類成員,而靜態方法則不行,能夠通過類名間接訪問。

staticmethod無需參數,classmethod須要類變量作為參數傳遞(不是類的實例)。類函數另一個用途就是能夠對類屬性進行改動


7.變量訪問屬性:C++的面向對象的語法都比較的規範,有公有、私有和保護的數據類型,而python類是沒有權限控制的,全部變量都是能夠被外部調用,盡管我們能夠在變量或者方法的面前加上雙下滑線__,表示私有訪問權限,事實上在內部實現上。是將私有變量進行了轉化,規則是:_<類名>__<私有變量>。

但這個實際上是python的偽私有,僅僅是一種程序猿約定俗稱的規定。加了雙下劃線就表示私有變量,可是假設要在外部調用的話,還是能夠調用的。看以下的樣例:


class Test:
count = 0
def __init__(self,c):
self.count = c
self.__name="zhangzhe" #私有變量
self.__class__.count = self.__class__.count +1

def __get_name(self): #私有方法
return self.__name

def get_counte(self):
return self.count


a = Test(3)
print a.count #3
#print a.name #AttributeError:Test instance has no attribute ‘name‘
print Test.count #1
print a.get_counte() #3

print a.__dict__ #{‘count‘: 3, ‘_Test__name‘: ‘zhangzhe‘}
print a._Test__name #zhangzhe


print Test.__dict__
#{‘count‘: 1, ‘__module__‘: ‘classdemo02‘, ‘_Test__get_name‘: <function __get_name at 0x101e92500>, ‘__doc__‘: None, ‘__init__‘: <function __init__ at 0x101e92488>, ‘get_counte‘: <function get_counte at 0x101e92578>}
print a._Test__get_name() #zhangzhe


b = Test(-1)
print b.count #1
print Test.count #2


這裏定義的__name是私有屬性,__get_name()是私有方法,直接訪問的話,會提示找不到相關的屬性或者方法。能夠通過使用 a._Test__name和a._Test__get_name()來訪問私有的變量和方法。
註:在Python中沒有像C++中public和private這些keyword來差別公有屬性和私有屬性,它是以屬性命名方式來區分,假設在屬性名前面加了2個下劃線‘__‘,則表明該屬性是私有屬性,否則為公有屬性
小結: 對於類屬性和實例屬性,假設在類方法中引用某個屬性。該屬性必然是類屬性,而假設在實例方法中引用某個屬性(不作更改)。而且存在同名的類屬性。此時若實例對象有該名稱的實例屬性,則實例屬性會屏蔽類屬性。即引用的是實例屬性,若實例對象沒有該名稱的實例屬性。則引用的是類屬性。假設在實例方法更改某個屬性,而且存在同名的類屬性。此時若實例對象有該名稱的實例屬性,則改動的是實例屬性,若實例對象沒有該名稱的實例屬性。則會創建一個同名稱的實例屬性。想要改動類屬性,假設在類外。能夠通過類對象改動,假設在類裏面,僅僅有在類方法中進行改動。
  從類方法和實例方法以及靜態方法的定義形式就能夠看出來。類方法的第一個參數是類對象cls,那麽通過cls引用的必然是類對象的屬性和方法;而實例方法的第一個參數是實例對象self,那麽通過self引用的可能是類屬性、也有可能是實例屬性(這個須要詳細分析),只是在存在同樣名稱的類屬性和實例屬性的情況下,實例屬性優先級更高。靜態方法中不須要額外定義參數,因此在靜態方法中引用類屬性的話,必須通過類對象來引用。



從上面看來,Python是很的靈活 的。它的面向對象沒有做到真正的不能訪問。僅僅是一種約定讓大家去遵守,就像大家都用self來代表在類裏的當前對象。你也能夠用其它的。僅僅是大家習慣了用self。

三 類中的內置方法
除了上面的init和del方法外,Python類中還有例如以下的多個內置方法(類似於C++中的運算符重載): 技術分享

1.__new__():__new__()在__init__()之前被調用。用於生成實例對象。利用這種方法和類屬性的特性能夠實現設計模式中的單例模式。單例模式是指創建唯一對象嗎,單例模式設計的類僅僅能實例化一個對象。比如以下的代碼: __instance = None def __new__(cls, *args, **kwargs): #在init函數前被調用 if ZZClass.__instance is None: #生產唯一實例
print("ZZClass __new__ called.")
ZZClass.__instance = object.__new__(cls,*args,**kwargs)
return ZZClass.__instance

2.__getattr__()、__setattr__()和__getattribute__():當讀取對象的某個屬性時,python會自己主動調用__getattr__()方法。

比如,fruit.color將轉換為fruit.__getattr__(color)。當使用賦值語句對屬性進行設置時,python會自己主動調用__setattr__()方法。

__getattribute__()的功能與__getattr__()類似,用於獲取屬性的值。可是__getattribute__()能提供更好的控制,代碼更健壯。

註意,python中並不存在__setattribute__()方法


3. __str__()用於表示對象代表的含義。返回一個字符串.實現了__str__()方法後,能夠直接使用print語句輸出對象,也能夠通過函數str()觸發__str__()的運行。這樣就把對象和字符串關聯起來,便於某些程序的實現,能夠用這個字符串來表示某個類。
比如: def __str__(self): return "ZZClass itself."
4. __call__():在類中實現__call__()方法,能夠在對象創建時直接返回__call__()的內容。使用該方法能夠模擬靜態方法。 比如: class InternalClass:
def __call__(self, *args, **kwargs):
print "internal class."

func = InternalClass()
# 調用InternalClass(),此時將類InternalClass作為函數返回,
# 即為外部類ZZClass定義方法func(),func()將運行__call__()內的代碼。

myObj.func() #internal class. myObj.func() #internal class.
5.__getitem__():假設類把某個屬性定義為序列,能夠使用__getitem__()輸出序列屬性中的某個元素。
class Persons:
def __getitem__(self, item):
return self.persons[item]

allPersons = Persons()
allPersons.persons = ["Alice","Joe"]
print allPersons[1]

補充: 強類型語言
一種總是強制類型定義的語言,Java和Python是強制類型定義的,假設你有一個整數,不顯示地進行轉換,不能將其視為一個字符串。
弱類型定義語言
一種類型能夠被忽略的語言,與強類型定義相反。VBScript是弱類型定義 的。在VBScript中。能夠將字符串 ‘12‘ 和整數 3 進行連接得到字符串 ‘123‘, 然後能夠把它看成整數 123,而不須要顯示轉換


註意:強弱類型中心詞是‘類型’,而不是變量,一個變量是否可以綁定到多種類型,跟該語言是否強弱類型無關。
動態聯編 編譯程序在編譯階段並不能確切知道將要調用的函數,僅僅有在程序執行時才幹確定將要調用的函數,為此要確切知道該調用的函數。要求聯編工作要在程序執行時進行。這樣的在程序執行時進行聯編工作被稱為動態聯編。 事實上它不是 Python 的特性,全部面向對象的語言基本都須要實現。它使得運行一個對象的方法時。使用的是它自己(或它的類)的方法,而不是它的父類的方法。 動態聯編通常的實現方法是把函數的入口地址保存起放在一個表裏,(比如,c++的虛表),在執行時通過動態的查找表的方式來推斷應該調用哪個函數。

函數的執行效率沒有靜態聯編的高,但比其靈活。


靜態聯編 靜態聯編是指聯編工作出如今編譯連接階段,這樣的聯編又稱早期聯編,它在編譯時就攻克了程序中的操作調用與運行該操作代碼間的關系。


Python -面向對象(一 基本概念)