Python class設計· parallel_processing

文章推薦指數: 80 %
投票人數:10人

Python class設計. 建構子(constructor); 類別建構與初始化; str method; 用property 取代getters, setters; 類別屬性與實體屬性; static方法與class方法 ... parallel_processing Introduction 程序/行程(Process) 平行處理系統類別 MPI(Messagepassinginterface) MPICH安裝與設定 點對點通訊 集合通訊 Communicator One-sidedcommunication TORQUE OpenMP OpenMP設定 pthread ApacheSpark Spark安裝與設定 RDD介紹 基本程式設計 HDFS MapReduce介紹 CUDA 安裝(installation) 第一個程式 PyCUDA Theano Tensorflow XORproblem Cython 編譯 平行處理 PEP numpy Pandas C語言語法 Clibrary Pointer Doublepointer Functionpointer Struct Memoryleak C++參考 C++物件 C++樣版 x86函式調用協定 編譯器參數 cmake Pythonclass設計 nosetest py.test BLASlibrary Linpackbenchmark PoweredbyGitBook Pythonclass設計 Pythonclass設計 由於python不像c++一樣在設計時必須考慮記憶體的管理,因為在class的設計上複雜度已經下降許多。

pythonclass內部沒有public,protected,private的區間,全部都是public,因此若是希望為privatevalue時,應在變數前面加上兩個underline。

當Python看到一個attribute後,會到該object一個內建的attributedict中尋找,這個attribute是不是有定義,如果是data即回傳該variable的值,若是function則回傳該methodobject;假使找不到,則再往上一層,也就是class或superclassobject找。

classAccount: def__init__(self,number,name): #constructor self.number=number self.name=name self.balance=0 defdeposit(self,amount): #membershipfunction ifamount<=0: raiseValueError('mustbepositive') self.balance+=amount defwithdraw(self,amount): #membershipfunction ifamount<=self.balance: self.balance-=amount else: raiseRuntimeError('balancenotenough') def__del__(self): #destructor return"byebye" #建立實例 acct=Account('123-456-789','Justin') acct.deposit(1000) print(acct.balance)#1000 acct.withdraw(500) print(acct.balance)#500 在class中desposit與withdraw都有self參數,為何呼叫時不見了?其原因是acct.deposit(value)等同於Account.deposit(acct,value)。

可用dir(ClassName)或dir(Instance)查看class的value以及所支援的所有method 由於instance已經呼叫了constructor,因此會多出number,name,以及balance三個屬性。

print(dir(Account)) ['__class__','__delattr__','__dict__','__dir__','__doc__', '__eq__','__format__','__ge__','__getattribute__','__gt__', '__hash__','__init__','__le__','__lt__','__module__','__ne__', '__new__','__reduce__','__reduce_ex__','__repr__','__setattr__', '__sizeof__','__str__','__subclasshook__','__weakref__', 'deposit','withdraw'] #instance較class多出number,name以及balance三個屬性 print(dir(acct)) ['__class__','__delattr__','__dict__','__dir__','__doc__', '__eq__','__format__','__ge__','__getattribute__','__gt__', '__hash__','__init__','__le__','__lt__','__module__','__ne__', '__new__','__reduce__','__reduce_ex__','__repr__','__setattr__', '__sizeof__','__str__','__subclasshook__','__weakref__', 'balance','deposit','name','number','withdraw'] 使用type()可查看class或instance的類別名稱 print(type(Account)) print(type(acct)) 建構子(constructor) 利用建構子(constructor)建立的物件被稱為實體(instance),實際上建立物件的整個過程是執行_init()方法(method)。

自行定義的類別會有預先定義好的\init_(),我們也可以改寫(override)成我們自己需要的。

凡是實體的方法都需要一個特別的參數--self,self是個預設的保留字(reservedword),所指的是實體本身自己,在init()所定義的實體屬性(attribute)都需要額外加上self。

類別建構與初始化 一般使用物件是用初始化函數_init,下面看看\init和\new_的聯繫和差別。

classA(object): def__init__(self,*args,**kwargs): print"init%s"%self.__class__ def__new__(cls,*args,**kwargs): print"new%s"%cls returnobject.__new__(cls,*args,**kwargs) a=A() 從輸出可以看到,當通過類產生實體一個物件的時候,_new方法首先被調用,然後是\init_方法。

對於_new和\init_可以概括為: _new方法在Python中是真正的構造方法(創建並返回實例),通過這個方法可以產生一個cls對應的實例物件,所以說\new_方法一定要有返回 對於_init方法,是一個初始化的方法,self代表由類產生出來的實例物件,\init_將對這個物件進行相應的初始化操作 _new_是在新式類中新出現的方法,它有以下行為特性: _new_方法是在類產生實體物件時第一個調用的方法,將返回實例物件 _new_”方法始終都是類的靜態方法(即第一個參數為cls),即使沒有被加上靜態方法裝飾器 第一個參數cls是當前正在產生實體的類,如果要得到當前類的實例,應當在當前類中的_new方法語句中調用當前類的父類的\new_方法 如果(新式)類中沒有重寫_new方法,Python默認是調用該類的直接父類的\new方法來構造該類的實例,如果該類的父類也沒有重寫\new,那麼將一直按照同樣的規則追溯至object的\new_方法,因為object是所有新式類的基類。

_new決定是否要使用該類的\init方法,因為\new_可以調用其他類的構造方法或者直接返回別的類創建的物件來作為本類的實例。

通常來說,新式類開始產生實體時,_new方法會返回cls(cls指代當前類)的實例,然後調用該類的\init方法作為初始化方法,該方法接收這個實例(即self)作為自己的第一個參數,然後依次傳入\new_方法中接收的位置參數和具名引數。

但是,如果_new沒有返回cls(即當前類)的實例,那麼當前類的init_方法是不會被調用的。

strmethod 類別(class)預設的str()方法(method)回傳實體(instance)物件(object)的字串(string)表達形式。

預設的方式是印出類別名稱,實體所在的記憶體位址。

classAccount: def__init__(self,number,name): #constructor self.number=number self.name=name self.balance=0 def__str__(self): return"{}_{}:{}".format(self.number,self.name,self.balance) #建立實例 acct=Account('123-456-789','Justin') acct.deposit(1000) print(acct)#等同print(acct.__str__()) #會呼叫__str__()得到如下: 123-456-789_Justin:500 用property取代getters,setters 官網有許多種寫法 寫Java的人習慣寫一堆getX()以及setX(),這在Python中是不被鼓勵的,pythonic方式是直接存取attribute,而當你需要進一步控制時,可以使用property達到getter以及setter效果,如此一來,客戶端的程式不需要修改就能正常運作。

property可將method當做attribute使用。

要避免對吃資源的method使用property,因為存取attribute給人的感覺是很輕量的操作,這樣使用該物件的人就有可能忽視效能的問題。

classPerson(object): def__init__(self,first_name,last_name): self.first_name=first_name self.last_name=last_name @property deffull_name(self): return"%s%s"%(self.first_name,self.last_name) person=Person("Mike","Driscoll") print(person.full_name)#'MikeDriscoll' 類別屬性與實體屬性 Python類別(class)的屬性(attribute)有兩種,一種是類別屬性(classattribute),另一種是實體屬性(instanceattribute). 兩者的可視範圍不同,instanceattribute只能在該instance被看見,而classattribute在同一class中的不同instnace均為可見。

classAccount: #classattribute,客戶數量 count=0 def__init__(self,number,name): #instanceatrtributes Account.count+=1 self.number=number self.name=name self.balance=0 兩者有何差別呢?通常類別屬性需要用類別名稱來存取,但若兩者的識別字(identifier)不同,實體物件(object)也可以存取類別屬性。

acct=Account('123-456-789','Justin') acct2=Account('123-456-790','John') print(Account.count)#0 acct=Account('123-456-789','Justin') print(Account.count)#1 acct2=Account('123-456-790','John') print(Account.count)#2 print(acct.count)#2 print(acct2.count)#2 static方法與class方法 static方法的作用與函數(function)相同,也就是說不需要建立實體物件(instance)就可以使用,然而static方法仍是類別的一部分,因此呼叫(call)類別方法需連用類別名稱。

static方法定義時沒有參數 類別方法需要一個特別的參數(parameter),習慣上使用cls,這與實體方法的self類似,不同的是cls用來存取類別的屬性(attribute)。

需要注意的是可利用類別名稱呼叫static方法與類別方法,也可以利用實體物件呼叫兩者。

classAccount: def__init__(self,number,name): self.number=number self.name=name self.balance=0 @staticmethod defstatic_func(): return("thisisstaticmethod.") @classmethod defclass_func(cls): return("thisisclassmethodof{}".format(cls.__name__)) print(Account.static_func())#thisisstaticmethod. print(Account.class_func())#thisisclassmethodofAccount acct=Account('123-456-789','Justin') print(acct.static_func())#thisisstaticmethod. print(acct.class_func())#thisisclassmethodofAccount 類別屬性封裝 Python類別(class)的屬性(attribute)權限預設是公開的,因此類別以外的地方也可以存取。

這些類別以外的地方,直接以句點運算子存取屬性值,然而有時候我們希望做到良好的資訊隱藏(informationhiding),也就是說我們不要讓類別定義以外的地方,存取屬性值,這時,我們可以將屬性設定為私有的,簡單來說,就是在屬性識別字(identifier)名稱前加上連續兩個底線符號。

這樣一來,若是要在類別以外的地方使用屬性值,需要另外可存取的公開方法(method) classAccount: def__init__(self,number,name): Account.count+=1 self.__number=number self.__name=name self.__balance=0 defdeposit(self,amount): ifamount<=0: raiseValueError('mustbepositive') self.__balance+=amount defwithdraw(self,amount): ifamount<=self.__balance: self.__balance-=amount else: raiseRuntimeError('balancenotenough') defget_balance(self): returnself.__balance // acct=Account('123-456-789','Justin') #print(acct.__balance)#AttributeError:'Account'objecthasnoattribute'__balance' print(acct.get_balance())#0 類別繼承 繼承的格式如下,類別名稱後的小括弧中註明父類別,可多重繼承。

ChildA的constructor中直接指定Base,優點是明確,缺點是若Base更名或是ChildA繼承自不同物件時也要連動改名。

ChildB、ChildC使用了super指定的其繼承自何方,可避免上述缺點。

使用super還有一個原因是dependencyinjectionStackoverflowdiscussion isinstance()用於檢查class類型,若物件類型與Class相同,則回True issubclass()用於檢查繼承,若某一Class是繼承,則回True classChildA(Base): def__init__(self): Base.__init__(self) classChildB(Base): def__init__(self): super(ChildB,self).__init__() classChildC(Base): def__init__(self): #OKinpython3.x super().__init__() 子類別(subclass)可依本身特性設定自己的屬性(attribute)與方法(method),也會從父類別(superclass)繼承(inherit)屬性與方法。

一般來說,沒有設定成私有的屬性及方法都會被繼承,子類別可由父類別公開的方法存取父類別私有的屬性。

子類別也可依需要改寫(override)父類別的方法,這是說子類別需要用到與父類別具有相同名稱的方法,但是子類別需要的功能有所修改、擴充或增加,因此當子類別裡頭定義與父類別相同名稱的方法時,就會改寫父類別的方法。

經過改寫,子類別的方法完全屬於子類別所有. Demo為父類別,定義四個方法,SubDemo為子類別,改寫Demo的兩個方法,包括init()與str()。

classDemo: __x=0 def__init__(self,i): self.__i=i Demo.__x+=1 def__str__(self): returnstr(self.__i) defhello(self): print("hello"+self.__str__()) @classmethod defgetX(cls): returncls.__x classSubDemo(Demo): def__init__(self,i,j): super().__init(i) self.__i=i self.__j=j def__str__(self): #這裡的__str__(),在return後的運算式(expression) #先呼叫super().__str__(),因為self.__i已經變成父類別 #Demo私有屬性,因此需要先呼叫父類別的__str__()。

returnstr(self.__i)+"+"+str(self.__j) a=SubDemo(1234) a.hello() print("a.__x=",a.getX()) b=SubDemo(56,78) b.hello() print("b.__x=",b.getX()) print() print("a.__x=",a.getX()) print("b.__x=",b.getX()) 多重繼承 設計類別(class)時,父類別(superclass)可以有多個,這是說子類別(subclass)能夠繼承(inherit)多個父類別,使子類別可以有多種特性。

多重繼承(mutlipleinheritance)的使用很簡單,寫父類別的小括弧中,利用逗點分開每個父類別即可。

當子類別繼承(inheritance)超過一個來源的時候,會以寫在最左邊的父類別優先繼承,這是說,多個父類別如果有相同名稱的屬性(attribute)與方法(method),例如init()、str()等,就會以最左邊的父類別優先。

classSubClass(SuperClass1,SuperClass2): pass 多型 多型可使物件的型態具有通用的效力 SubDemo1與SubDemo2繼承(inherit)自Demo,因此兩個子類別也都繼承了hello()方法(method),雖然SubDemo1與SubDemo2所建立的物件各自是不同型態,然而由繼承關係兩種型態可通用hello()。

classDemo: def__init__(self,i): self.i=i def__str__(self): returnstr(self.i) defhello(self): print("hello"+self.__str__()) classSubDemo1(Demo): def__init__(self,i,j): super().__init__(i) self.j=j def__str__(self): returnsuper().__str__()+str(self.j) classSubDemo2(Demo): def__init__(self,i,j): super().__init__(i) self.j=j self.k=str(self.i)+str(self.j) def__str__(self): returnself.k a=SubDemo1(22,33) b=SubDemo2(44,"55") a.hello() b.hello() ClassPrivate跟ModulePrivate的差別 在Python中雖然有Privatevariable,但是不像一般程式語言一樣具有強制的效力,如果外界執意要使用還是可以呼叫的到。

在StackOverflow有很清楚的解釋其中的差別。

Moduleprivate是指是以一個下底線_開頭的變數,例如_number或_getNumber(). 以此例子來說,我們在moduleA.py裡面定義了一個Moduleprivate_foo(),因為Python有個規定是在Import的時侯會略過所有以underscore開頭的成員,所以_foo()會被忽略,在執行print_foo()的時侯就會發生Exception。

但是如果執意要用還是可以透過Import__foo,得到想要的結果 #Moduleprivate,moduleA.py def_foo(): return'hi' frommoduleAimport* print_foo() ClassPrivate基本上是以二個下底線開頭,而且不超過一個下底線_結尾的的變數名稱,例如number或__getNumber(),但是init就不是classprivate,但這種兩個下底線開頭和結尾叫Magicmethods有其特別的用途,但這個不在本文討論範圍。

dir(f)是輸出:>['Fooaoo','_Fooboo','doc','module','coo']_boo被加上_Foo的Prefix,這種行為稱做NameMangling,多加上ClassName在前面。

所以透過f.boo()就沒辦法直接呼叫,但是透過加上ClassName的Prefix就可以了,例如printf._Fooboo(). #classprivate classFoo(): __aoo=123 def__boo(self): return123 defcoo(self): return456 f=Foo() printdir(f) #['_Foo__aoo','_Foo__boo','__doc__','__module__','coo'] printf._Foo__boo() printf.__boo() 總結 Moduleprivate:_number 達到Private的效果:Import除非明確把name打出來,不然fromAimport*時會自動略過所有以一個下底線_開頭的變數 使用時機:在Modulelevel下,告知外界,這些變數/函式是Private性質的,不建議外界直接存取 強制使用方式:直接Import即可,例如:fromAimport_Number ClassPrivate:__number 達到Private的效果:Class物件被建立的時侯,所有__XXX會被namemangling成__ClassName__XXX 使用時機:跟Moduleprivate一樣,就是表現出這些成員是內部使用的,不建議外界直接存取 強制使用方式:使用的時侯,加上Classname的的prefix 繼承的時侯,這些被namemangled的成員也會被繼承,但是是使用Parent的ClassName的Prefix,要注意一下。

在Python的世界中,PrivateVariable沒辦法強制讓外界無法使用,透過Naming可以讓別人知道這些PrivateVariable不應該直接被存取,避免誤用的Bug發生。

但是如果執意要呼叫,都還是有方法的。

直接對實例設值 python可對自訂class中隨時新增參數值。

classObject(object): pass Obj=Object() #直接設值 Obj.name="whatever" print(Obj.name)#whatever 但是若class有定義_slot_屬性時,則無法新增參數值。

擁有_slots屬性的類在產生實體物件時不會自動分配\dict,而obj.attr即obj.\dict_['attr'],所以會引起AttributeError 對於擁有_slots屬性的類的實例Obj來說,只能對Obj設置\slots_中有的屬性. classObject(object): __slots__={'a','b'} Obj=Object() Obj.name="whatever AttributeError:'Object'objecthasnoattribute'name'" Traceback(mostrecentcalllast): File"",line1,in Obj.a=1 print(Obj.a)#1 如何印出一個物件的所有屬性和方法 這個做法其實就是theObject._dict,也就是vars(obj)其實就是返回了o.\dict_ forproperty,valueinvars(theObject).iteritems(): printproperty,":",value 什麼是metaclass stackoverflow Metaclass是創建class的東西。

,而一個class是用來創建物件。

但是我們知道,Python中的類也是物件。

Metaclass就是用來創建類這些物件的,它們是類的類,你可以形象化地理解為: MyClass=MetaClass() MyObject=MyClass() Mixin樣式 Mixin模式是一種在python裡經常使用的模式,適當合理的應用能夠達到複用代碼,合理組織代碼結構的目的。

Python的Mixin模式可以通過多重繼承的方式來實現。

Mixins與Traits這兩個名詞,技術上來說其實有些不同,然而不同語言可能也會有不同的表現,因此在這邊暫且當它們是同義詞,重點在瞭解:Mixins、Traits實際上是一種在多重繼承與單一繼承間的妥協,是一種受限的多重繼承,在繼承來源為抽象的共用實作時,可避免多重繼承時的許多問題,然而,它並不能解決所有的問題。

使用Mixins,一個顯見的問題是,仍舊有可能發生名稱衝突的問題。

另一個隱式的問題是,Mixins的來源與被Mixin的對象之間,實際上會建立起依賴關係,被Mixin的對象中,必須實現Mixins預期之協定,通常是某個或某些方法名稱,視情況而定。

使用Mixin類實現多重繼承要非常小心 先它必須表示某一種功能,而不是某個物品,如同Java中的Runnable,Callable等 其次它必須責任單一,如果有多個功能,那就寫多個Mixin類 然後,它不依賴於子類的實現 最後,子類即便沒有繼承這個Mixin類,也照樣可以工作,就是缺少了某個功能。

(比如飛機照樣可以載客,就是不能飛了^_^) 把一些基礎和單一的功能比如一般希望通過interface/protocol實現的功能放進Mixin模組裡面還是不錯的選擇。

mixin模組自帶實現。

在使用的時候一般把mixin的類放在父類的右邊似乎也是為了強調這並不是典型的多繼承,是一種特殊的多繼承,而是在繼承了一個基類的基礎上,順帶利用多重繼承的功能給這個子類添點料,增加一些其他的功能。

保證Mixin的類功能單一具體,混入之後,新的類的MRO樹其實也會相對很簡單,並不會引起混亂。

classCommonEqualityMixin(object): def__eq__(self,other): return(isinstance(other,self.__class__) andself.__dict__==other.__dict__) def__ne__(self,other): returnnotself.__eq__(other) classFoo(CommonEqualityMixin): def__init__(self,item): self.item=item 物件使用Mixin的繼承依然遵守”is-a”關係,從含義上看依然遵循單繼承的原則。

下面的Airplane類實現了多繼承,不過它繼承的第二個類我們起名為PlaneMixin,而不是Plane,這個並不影響功能,但是會告訴後來讀代碼的人,這個類是一個Mixin類。

所以從含義上理解,Airplane只是一個Vehicle,不是一個Plane。

這個Mixin,表示混入(mix-in),它告訴別人,這個類是作為功能添加到子類中,而不是作為父類,它的作用同Java中的介面。

classVehicle(object): pass classPlaneMixin(object): deffly(self): print'Iamflying' classAirplane(Vehicle,PlaneMixin): pass Mixin強調的是功能而不像繼承那樣包括所有功能和資料欄,但利用mixin同樣也可以實現代碼複用,下面這段代碼來自StackOverflow,當然functools.total_ordering()裝飾器已經提供相同功能了,這裡僅用來說明mixin實現代碼複用。

lassComparable(object): def__ne__(self,other): returnnot(self==other) def__lt__(self,other): returnself<=otherand(self!=other) def__gt__(self,other): returnnotself<=other def__ge__(self,other): returnself==otherorself>other classInteger(Comparable): def__init__(self,i): self.i=i classChar(Comparable): def__init__(self,c): self.c=c abstractclass 如果一個類別定義時不完整,有些狀態或行為必須留待子類別來具體實現,則它是個抽象類別(AbstractClass)。

例如,在定義銀行帳戶時,你也許想將一些帳戶的共同狀態與行為定義在父類別中。

顯然地,這個類別的定義不完整,self.id、self.name、self.balance沒有定義,嘗試使用這個類別進行操作時,就會發生直譯錯誤. 你可以繼承這個類別來實作未完整的定義,現在的問題是,實際上開發人員還是可以用Account()實例化。

可以修改一下Account的定義要求使用者繼承後必須實做方法。

classAccount: def__init__(self): raiseNotImplementedError("Accountisabstract") defwithdraw(self,amount): ifamount>=self.balance: self.balance-=amount else: raiseValueError('餘額不足') def__str__(self): return('Id:\t\t'+self.id+ '\nName:\t\t'+self.name+ '\nBalance:\t'+str(self.balance)) acct=Account() #print(acct)error 由直譯錯誤是一種方式,實際上視你的需求而定(是否可實例化、子類別是否定義初始化方法等),還有許多模擬的方式,不過在Python中,可以使用Metaclass與@abstractmethod來達到規範的需求。

在描述流程輸廓時,並沒有提及如何顯示訊息、沒有提及如何取得使用者輸入等具體的作法,只是歸納出一些共同的流程步驟: importrandom fromabcimportABCMeta,abstractmethod classGuessGame(metaclass=ABCMeta): @abstractmethod defmessage(self,msg): pass @abstractmethod defguess(self): pass defgo(self): self.message(self.welcome) number=int(random.random()*10) whileTrue: guess=self.guess(); ifguess>number: self.message(self.bigger) elifguess



請為這篇文章評分?