# coding:utf-8程序員
#-------------------------------------------------- 對象魔力 -----------------------------------------express
#多態app
#多態方法dom
print 'abc'.count('a')ide
#1函數
print [1, 2, 'a'].count('a')工具
#1spa
#不用關心count('a')裏面是什麼類型數據,只要知道count方法,能夠返回a出現多少次,就好了,這就是多態方法設計
#choice函數,能夠從序列中隨機選出元素。對象
from random import choice
x = choice(['Hello, world!', [1, 2, 'e', 'e', 4]])
#x多是Hello,world字符串,多是[1, 2, 'e', 'e', 4]列表,不用關心究竟是哪一個類型,只要關心變量x中字符e出現了多少次。無論x是字符串仍是列表,均可以使用count函數。
print x.count('e')
#2 #本例中,看來列表勝出了。
#多態多種形式
#當面對對象不知道是什麼類型,但又要對對象作些何時,就會用到多態
#這不只限於方法,不少內建運算符和函數都有多態的性質。好比:
print 1 + 2
#3
print 'Fish' + 'license'
#Fishlicense
#這裏的加運算符對於數字和字符串都能起做用。
#爲說明這一點,假設有個叫作add的函數,它能夠將兩個對象相加。能夠直接將其定義成上面的形式。
def add(x, y):
return x+y
#對於不少類型的參數均可以用:
print add(1, 2)
#3
print add('Fish', 'license')
#Fishlicense
#若是須要編寫打印對象長度消息的函數,只需對象具備長度(len函數可用)便可。
def length_message(x):
print "The length of", repr(x), "is", len(x)
#能夠看到,函數中還使用了repr函數,repr函數是多態特性。
length_message('Fnord')
#The length of 'Fnord' is 5
length_message([1, 2, 3])
#The length of [1, 2, 3] is 3
#不少函數和運算符都是多態的---你寫的絕多數程序可能都是,即使你並不是有意這樣。只要使用多態函數和運算符,就會與「多態」發生關聯。
#事實上,惟一能毀掉多態的就是使用函數檢查類型,好比type,isinstance以及issubclass函數等。
#若是可能的話,應該盡力避免使用這些毀掉多態的方式。真正重要的是如何讓對象按照你所但願的方式工做,無論它是不是正確的類型(或者類)。
#封裝
#封裝是指向程序中的其餘部分隱藏對象的具體實現細節原則。
#接下面再論私有化進行詳細解釋
#繼承
#就是函數,類等調用,相同代碼不想重複寫,直接調用給其餘代碼,就是繼承。
#-------------------------------------------- 類和類型 ------------------------------------------
#建立類
__metaclass__ = type #肯定使用新式類,一種寫法格式,建立類寫上便可
class Person:
def setName(self, name):
self.name = name
def getName(self):
return self.name
def greet(self):
print "Hello, world! I'm %s." % self.name
#這個例子包含3個方法定義,除了它們是寫在class語句裏,一切都像是函數定義。Person是類的名字。class語句會在函數定義的地方建立本身的命名空間。
#self參數看起來有點奇怪,它是對於對象自身的引用。那麼它是什麼對象?建立一些實例看看:
foo = Person()
bar = Person()
foo.setName('Luke Skywalker') #foo將本身爲第一個參數傳入到self裏
bar.setName('Anakin Skywalker')
foo.greet()
#Hello, world! I'm Luke Skywalker.
bar.greet()
#Hello, world! I'm Anakin Skywalker.
#在調用foo的setName和greet函數時,foo自動將本身做爲第一個參數傳入函數中---所以形象地命名爲self。對於這個變量,每一個人可能都會有本身的叫法,可是由於它老是對象本身,因此習慣上老是叫作self
#顯然這就是self的用處和存在的必要性。沒有它的話,成員方法就無法訪問它們要對其特性進行操做的對象自己了。
#特性能夠在外部訪問:
#就是setName函數裏的self.name = name
print foo.name
#Luke Skywalker
bar.name = 'Yoda'
bar.greet()
#Hello, world! I'm Yoda.
#若是知道foo是Person的實例的話,那麼能夠把foo.greet()寫成:
Person.greet(foo) #方便的簡寫
#Hello, world! I'm Luke Skywalker.
#特性,函數,方法
__metaclass__ = type
class Class:
def method(self):
print 'I have a self!'
def function():
print "I don't..."
instance = Class() #調用類
instance.method() #instance是實例,method是方法,有self參數
#I have a self!
instance.method = function #綁定到一個普通函數上,沒有self參數
instance.method()
#I don't...
__metaclass__ = type
class Bird:
song = 'Squaawk!'
def sing(self):
print self.song
bird = Bird()
bird.sing()
#Squaawk!
birdsong = bird.sing
birdsong() #調用方法和函數十分類似,但變量綁定方法bird.sing仍是會對self參數進行訪問
#Squaawk!
#再論私有化,接上面的封裝
#使用上面類的例子:
__metaclass__ = type
class Person:
def setName(self, name):
self.name = name
def getName(self):
return self.name
def greet(self):
print "Hello, world! I'm %s." % self.name
o = Person() #調用類
o.setName('Sir Lancelot')
print o.getName()
#Sir Lancelot
#變量o綁定到對象(Person類)上,可使用setName和getName方法。一切看起來很完美。可是假設變量o將它的名字存儲在全局變量globalName中:
#但假設變量o將它的名字存儲在全局變量globalName中:
#globalName
#'Sir Lancelot'
#這意味着在使用OpenObject類的實例時候,不得不關心globalName的內容。實際上要確保不會對它進行任何更改:
#globalName = 'Sir Gumby'
#o.getName()
#'Sir Gumby'
#若是建立了多個OpenObject實例的話就會出現問題,由於變量相同,因此可能會混淆:
#o1 = OpenObject()
#o2 = OpenObject()
#o1.setName('Robin Hood')
#o2.getName()
#'Robin Hood'
#設定一個名字後,其餘名字也自動設定了。
#爲了讓方法或者特性變成私有(外部沒法訪問),只要在它的名字前面加上雙下劃線便可。
__metaclass__ = type
class Secretive:
def __inaccessible(self): #讓方法變成私有化(外部沒法訪問),只要在它的名字前面加上雙下劃線便可
print "Bet you can`t see me..."
def accessible(self):
print "The secret message is:"
self.__inaccessible() #內部調用
s = Secretive()
# s.__inaccessible() #報錯沒法調用
s.accessible() #內部能夠調用
#The secret message is:
#Bet you can`t see me...
s._Secretive__inaccessible() #也有外部調用方法,但不該該這麼作
#Bet you can`t see me...
#類的命名空間
__metaclass__ = type
class MemberCounter:
members = 0
def init(self):
MemberCounter.members += 1
#上面代碼中,在類做用域內定義了一個可供全部成員(實例)訪問的變量(members),用來計算類的成員數量。注意init用來初始化全部實例。
m1 = MemberCounter() #調用類
m1.init()
print MemberCounter.members
#1
m2 = MemberCounter()
m2.init()
print MemberCounter.members
#2
#類做用域內的變量也能夠被全部實例訪問:
print m1.members
#2
print m2.members
#2
m1.members = 'Two' #新的numbers值被寫到了m1特性中,屏蔽了類範圍內的變量。這跟函數內的局部和全局變量的行爲十分相似。
print m1.members
#Two
print m2.members
#2
#指定超類
__metaclass__ = type
class Filter: #寫一個過濾做用的類(超類)
def init(self):
self.blocked = []
def filter(self, sequence):
return [x for x in sequence if x not in self.blocked] # 如何理解這句話,同等於下面註釋
# l = []
# for x in sequence:
# if x not in self.blocked:
# l.append(x)
# return l
#Filter是個用於過濾序列的通用類,事實上它自身不能過濾任何東西
f = Filter()
f.init()
print f.filter([1, 2, 3])
#[1, 2, 3]
#Filter類的用處在於它能夠用做其餘類的(基類)超類,好比下面SPAMFilter類,能夠將序列中的'SPAM'過濾出去。
class SPAMFilter(Filter): #繼承上面的類,能夠不用寫一大堆過濾的類,從上面繼承更加方便。SPAMFilter是Filter的子類,Filter是SPAMFilter的超類
def init(self): #重寫超類Filter中的init方法
self.blocked = ['SPAM'] #原本過濾關鍵字沒寫,如今寫入SPAM
s = SPAMFilter()
s.init()
print s.filter(['SPAM','SPAM','SPAM','eggs','bacon','SPAM'])
#['eggs', 'bacon']
#檢查繼承
#若是想要查看一個類是不是另一個的子類,可使用內建的issubclass的函數:
print issubclass(SPAMFilter, Filter) #查看SPAMFilter是不是Failter的子類
#True
print issubclass(Filter, SPAMFilter)
#False
#若是想要知道已知類的基類(們)(注意下節解釋),可使用特殊特性__bases__:
print SPAMFilter.__bases__ #已知類的基類,使用__bases__
#(<class '__main__.Filter'>,)
print Filter.__bases__
#(<type 'object'>,)
#使用isinstance方法檢查一個對象是不是一個類的實例:
s = SPAMFilter()
print isinstance(s, SPAMFilter)
#True
print isinstance(s, Filter)
#True
print isinstance(s, str)
#False
#想知道一個對象屬於哪一個類,可使用__class__特性
print s.__class__
#<class '__main__.SPAMFilter'>
#多個超類
#上節提到一個類的基類(們),也就暗示它的基類可能會多於一個。事實上就是這樣,創建幾個新類來試試:
class Calculator:
def calculate(self,expression):
self.value = eval(expression)
class Talker:
def talk(self):
print 'Hi, my value is', self.value
class TalkingCalculator(Calculator, Talker):
pass
#子類(TalkingCalculator)本身不作任何事,它從本身的超類繼承全部的行爲。它從Calculator類那裏繼承calculate方法,從Talker類那裏繼承talk方法,這樣它就成了會說話的計算器。
tc = TalkingCalculator()
tc.calculate('1+2*3')
tc.talk()
#Hi, my value is 7
#這種行爲稱爲多重繼承,是個很是有用的工具。但除非特別熟悉多重繼承,不然應該儘可能避免使用,由於有些時候會出現不可預見的麻煩。
#須要注意是若是一個方法從多個超類繼承(也就是說有兩個具備相同名字的不一樣方法),那麼必須注意下超類的順序(在class語句中):先繼承的類中的方法會重寫後繼承的類中的方法。
#若是前例中Calculator:類也有個叫talk方法(函數),那麼它會覆蓋Talker的talk方法(使其沒法訪問)。若是把它們順序掉過來,像下面這樣:
#class TalkingCalculator(Talker, Calculator): pass
#就會讓Talker的talk方法可用了。
#接口和內省
#能夠查看方法是否存在(接口)
print hasattr(tc, 'talk')
#True
print hasattr(tc, 'fnord')
#False
#小結
#對象
#對象包括特性和方法。特性只是做爲對象的一部分的變量,方法則是存儲在對象內的函數。(綁定)方法和其餘函數的區別在於方法老是將對象做爲本身的第一個參數,這個參數通常稱爲self。
#類
#類表明對象的集合(或一類對象),每一個對象(實例)都有一個類。類的主要任務是定義它的實例會用到方法。
#多態
#多態是實現將不一樣類型和類的對象進行一樣對待的特性---不須要知道對象屬於哪一個類就能調用的方法。
#封裝
#對象能夠將它們的內部狀態隱藏(或封裝)起來。在一些語言中,這意味着對象的狀態(特性)只對本身的方法可用。在Python中,全部的特性都是公開可用的,可是程序員應該在直接訪問對象狀態時謹慎行事,由於他們可能無心中使得這些特性在某些方面不一致。
#繼承
#一個類能夠是一個或者多個類的子類。子類從超類繼承全部方法。可使用多個超類,這個特性能夠用來組成功能的正交部分(沒有任何聯繫)。普通的實現方式是使用核心的超類和一個或者多個混合的超類。
#接口和內省
#通常來講,對於對象不用探討過深。程序猿能夠靠多態調用本身須要的方法。不過若是想要知道對象到底有什麼方法和特性,有些函數能夠幫助完成這項工做。
#面向對象設計
#關於如何(或者說是否應該進行)面向對象設計有不少的觀點。無論你持什麼觀點,徹底理解這個問題,而且建立容易理解的設計是很重要的。