楔子 你現在是一家游戲公司的開發人員,現在需要你開發一款叫做<人狗大戰>的游戲,你就思考呀,人狗作戰,那至少需要2個角色,一個是人, 一個是狗,且人和狗都有不同的技能,比如人拿棍打狗, 狗可以咬人,怎麼描述這種不同的角色和他們的功能呢? 你搜羅了自己掌握的所有技能,寫出了下麵的代碼來描述這兩個角色 ...
楔子
你現在是一家游戲公司的開發人員,現在需要你開發一款叫做<人狗大戰>的游戲,你就思考呀,人狗作戰,那至少需要2個角色,一個是人, 一個是狗,且人和狗都有不同的技能,比如人拿棍打狗, 狗可以咬人,怎麼描述這種不同的角色和他們的功能呢? 你搜羅了自己掌握的所有技能,寫出了下麵的代碼來描述這兩個角色def person(name,age,sex,job):
data = {
'name':name,
'age':age,
'sex':sex,
'job':job
}
return data
def dog(name,dog_type):
data = {
'name':name,
'type':dog_type
}
return data
上面兩個方法相當於造了兩個模子,游戲里的每個人和每條狗都擁有相同里的屬性。游戲開始,你根據一個人或一隻狗傳入的具體信息來塑造一個具體的人或者狗,怎麼生成呢?
d1 = dog("李磊","京巴")
p1 = person("嚴帥",36,"F","運維")
p2 = person("egon",27,"F","Teacher")
兩個角色對象生成了,狗和人還有不同的功能呀,狗會咬人,人會打狗,對不對? 怎麼實現呢,。。想到了, 可以每個功能再寫一個函數,想執行哪個功能,直接 調用 就可以了,對不?
def bark(d):
print("dog %s:wang.wang..wang..."%d['name'])
def walk(p):
print("person %s is walking..." %p['name'])<br><br>
walk(p1)
bark(d1)
上面的功能實現的簡直是完美!
但是仔細玩耍一會,你就不小心幹了下麵這件事
p1 = person("嚴帥",36,"F","運維")
bark(p1) #把人的對象傳給了狗的方法
事實 上,從你寫的代碼上來看,這並沒出錯。很顯然,人是不能調用狗的功能的,但在你的程式例沒有做限制,如何在代碼級別實現這個限制呢?
def person(name,age,sex,job): def walk(p): print("person %s is walking..." % p['name']) data = { 'name':name, 'age':age, 'sex':sex, 'job':job, 'walk':walk } return data def dog(name,dog_type): def bark(d): print("dog %s:wang.wang..wang..."%d['name']) data = { 'name':name, 'type':dog_type, 'bark':bark } return data限制功能全新代碼
d1 = dog("李磊","京巴") p1 = person("嚴帥",36,"F","運維") p2 = person("egon",27,"F","Teacher")生產具體的人和汪
d1['bark'](p1) #把人傳給了狗的方法無法調用了
你是如此的機智,這樣就實現了限制人只能用人自己的功能啦。
剛剛你用的這種編程思想其實就是簡單的面向對象編程,我們創造了兩個模子表示游戲里所有的人和狗之後,剩下的狗叫或者人走對於這兩個模子來說就不重要了。具體人he狗之間的交互就等著你去使用了。假如你和狗打起來了,這時候你是走路還是拿棍子打狗就由你自己決定了。那你的每一個決定可能都影響著你這場游戲的輸贏。這也是不確定的。和我們之前寫代碼按部就班的走,最終都會實現我們要完成的事情不太一樣了。
儘管如此,我們也只完成了這個游戲非常小的一部分。還有很多功能都沒有實現。
剛纔你只是阻止了兩個完全 不同的角色 之前的功能混用, 但有沒有可能 ,同一個種角色,但有些屬性是不同的呢? 比如 ,大家都打過cs吧,cs里有警察和恐怖份子,但因為都 是人, 所以你寫一個角色叫 person(), 警察和恐怖份子都 可以 互相射擊,但警察不可以殺人質,恐怖分子可以,這怎麼實現呢? 你想了說想,說,簡單,只需要在殺人質的功能裡加個判斷,如果是警察,就不讓殺不就ok了麽。 沒錯, 這雖然 解決了殺人質的問題,但其實你會發現,警察和恐怖分子的區別還有很多,同時又有很多共性,如果 在每個區別處都 單獨做判斷,那得累死。
你想了想說, 那就直接寫2個角色吧, 反正 這麼多區別, 我的哥, 不能寫兩個角色呀,因為他們還有很多共性 , 寫兩個不同的角色,就代表 相同的功能 也要重寫了,是不是我的哥? 。。。
好了, 話題就給你點到這, 再多說你的智商也理解不了了!
面向過程 VS 面向對象
面向過程的程式設計的核心是過程(流水線式思維),過程即解決問題的步驟,面向過程的設計就好比精心設計好一條流水線,考慮周全什麼時候處理什麼東西。
優點是:極大的降低了寫程式的複雜度,只需要順著要執行的步驟,堆疊代碼即可。
缺點是:一套流水線或者流程就是用來解決一個問題,代碼牽一發而動全身。
應用場景:一旦完成基本很少改變的場景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
面向對象的程式設計的核心是對象(上帝式思維),要理解對象為何物,必須把自己當成上帝,上帝眼裡世間存在的萬物皆為對象,不存在的也可以創造出來。面向對象的程式設計好比如來設計西游記,如來要解決的問題是把經書傳給東土大唐,如來想了想解決這個問題需要四個人:唐僧,沙和尚,豬八戒,孫悟空,每個人都有各自的特征和技能(這就是對象的概念,特征和技能分別對應對象的屬性和方法),然而這並不好玩,於是如來又安排了一群妖魔鬼怪,為了防止師徒四人在取經路上被搞死,又安排了一群神仙保駕護航,這些都是對象。然後取經開始,師徒四人與妖魔鬼怪神仙互相纏鬥著直到最後取得真經。如來根本不會管師徒四人按照什麼流程去取。
面向對象的程式設計的
優點是:解決了程式的擴展性。對某一個對象單獨修改,會立刻反映到整個體系中,如對游戲中一個人物參數的特征和技能修改都很容易。
缺點:可控性差,無法向面向過程的程式設計流水線式的可以很精準的預測問題的處理流程與結果,面向對象的程式一旦開始就由對象之間的交互解決問題,即便是上帝也無法預測最終結果。於是我們經常看到一個游戲人某一參數的修改極有可能導致陰霸的技能出現,一刀砍死3個人,這個游戲就失去平衡。
應用場景:需求經常變化的軟體,一般需求的變化都集中在用戶層,互聯網應用,企業內部軟體,游戲等都是面向對象的程式設計大顯身手的好地方。
在python 中面向對象的程式設計並不是全部。
面向對象編程可以使程式的維護和擴展變得更簡單,並且可以大大提高程式開發效率 ,另外,基於面向對象的程式可以使它人更加容易理解你的代碼邏輯,從而使團隊開發變得更從容。
瞭解一些名詞:類、對象、實例、實例化
類:具有相同特征的一類事物(人、狗、老虎)
對象/實例:具體的某一個事物(隔壁阿花、樓下旺財)
實例化:類——>對象的過程(這在生活中表現的不明顯,我們在後面再慢慢解釋)
初識類和對象
python中一切皆為對象,類型的本質就是類,所以,不管你信不信,你已經使用了很長時間的類了
>>> dict #類型dict就是類dict
<class 'dict'>
>>> d=dict(name='eva') #實例化
>>> d.pop('name') #向d發一條消息,執行d的方法pop
'eva'
從上面的例子來看,字典就是一類數據結構,我一說字典你就知道是那個用{}表示,裡面由k-v鍵值對的東西,它還具有一些增刪改查的方法。但是我一說字典你能知道字典里具體存了哪些內容麽?不能,所以我們說對於一個類來說,它具有相同的特征屬性和方法。
而具體的{'name':'eva'}這個字典,它是一個字典,可以使用字典的所有方法,並且裡面有了具體的值,它就是字典的一個對象。對象就是已經實實在在存在的某一個具體的個體。
再舉一個其他的例子,通俗一點,比如你現在有一個動物園,你想描述這個動物園,那麼動物園裡的每一種動物就是一個類,老虎、天鵝、鱷魚、熊。他們都有相同的屬性,比如身高體重出生時間和品種,還有各種動作,比如鱷魚會游泳,天鵝會飛,老虎會跑,熊會吃。
但是這些老虎熊啥的都不是具體的某一隻,而是一類動物。雖然他們都有身高體重,但是你卻沒有辦法確定這個值是多少。如果這個時候給你一隻具體的老虎,而你還沒死,那你就能給他量量身高稱稱體重,這些數值是不是就變成具體的了?那麼具體的這一隻老虎就是一個具體的實例,也是一個對象。不止這一隻,其實每一隻具體的老虎都有自己的身高體重,那麼每一隻老虎都是老虎類的一個對象。
在python中,用變數表示特征,用函數表示技能,因而具有相同特征和技能的一類事物就是‘類’,對象是則是這一類事物中具體的一個。
類的相關知識
初識類
聲明
def functionName(args):
'函數文檔字元串'
函數體
'''
class 類名:
'類的文檔字元串'
類體
'''
#我們創建一個類
class Data:
pass
class Person: #定義一個人類
role = 'person' #人的角色屬性都是人
def walk(self): #人都可以走路,也就是有一個走路方法,也叫動態屬性
print("person is walking...")
類有兩種作用:屬性引用和實例化
屬性引用(類名.屬性)
class Person: #定義一個人類
role = 'person' #人的角色屬性都是人
def walk(self): #人都可以走路,也就是有一個走路方法
print("person is walking...")
print(Person.role) #查看人的role屬性
print(Person.walk) #引用人的走路方法,註意,這裡不是在調用
實例化
類名加括弧就是實例化,會自動觸發__init__函數的運行,可以用它來為每個實例定製自己的特征
class Person: #定義一個人類
role = 'person' #人的角色屬性都是人
def __init__(self,name):
self.name = name # 每一個角色都有自己的昵稱;
def walk(self): #人都可以走路,也就是有一個走路方法
print("person is walking...")
print(Person.role) #查看人的role屬性
print(Person.walk) #引用人的走路方法,註意,這裡不是在調用
實例化的過程就是類——>對象的過程
原本我們只有一個Person類,在這個過程中,產生了一個egg對象,有自己具體的名字、攻擊力和生命值。
語法:對象名 = 類名(參數)
egg = Person('egon') #類名()就等於在執行Person.__init__()
#執行完__init__()就會返回一個對象。這個對象類似一個字典,存著屬於這個人本身的一些屬性和方法。
查看屬性&調用方法
print(egg.name) #查看屬性直接 對象名.屬性名
print(egg.walk()) #調用方法,對象名.方法名()
關於self
self:在實例化時自動將對象/實例本身傳給__init__的第一個參數,你也可以給他起個別的名字,但是正常人都不會這麼做。
因為你瞎改別人就不認識
類屬性的補充
一:我們定義的類的屬性到底存到哪裡了?有兩種方式查看
dir(類名):查出的是一個名字列表
類名.__dict__:查出的是一個字典,key為屬性名,value為屬性值
二:特殊的類屬性
類名.__name__# 類的名字(字元串)
類名.__doc__# 類的文檔字元串
類名.__base__# 類的第一個父類(在講繼承時會講)
類名.__bases__# 類所有父類構成的元組(在講繼承時會講)
類名.__dict__# 類的字典屬性
類名.__module__# 類定義所在的模塊
類名.__class__# 實例對應的類(僅新式類中)
對象的相關知識
回到咱們的人狗大戰:現在我們需要對我們的類做出一點點改變
人類除了可以走路之外,還應該具備一些攻擊技能。
class Person: # 定義一個人類
role = 'person' # 人的角色屬性都是人
def __init__(self, name, aggressivity, life_value):
self.name = name # 每一個角色都有自己的昵稱;
self.aggressivity = aggressivity # 每一個角色都有自己的攻擊力;
self.life_value = life_value # 每一個角色都有自己的生命值;
def attack(self,dog):
# 人可以攻擊狗,這裡的狗也是一個對象。
# 人攻擊狗,那麼狗的生命值就會根據人的攻擊力而下降
dog.life_value -= self.aggressivity
對象是關於類而實際存在的一個例子,即實例
對象/實例只有一種作用:屬性引用
egg = Person('egon',10,1000)
print(egg.name)
print(egg.aggressivity)
print(egg.life_value)
當然了,你也可以引用一個方法,因為方法也是一個屬性,只不過是一個類似函數的屬性,我們也管它叫動態屬性。
引用動態屬性並不是執行這個方法,要想調用方法和調用函數是一樣的,都需要在後面加上括弧
print(egg.attack)
我知道在類里說,你可能還有好多地方不能理解。那我們就用函數來解釋一下這個類呀,對象呀到底是個啥,你偷偷的用這個理解就好了,不要告訴別人
def Person(*args,**kwargs):
self = {}
def attack(self,dog):
dog['life_value'] -= self['aggressivity']
def __init__(name,aggressivity,life_value):
self['name'] = name
self['aggressivity'] = aggressivity
self['life_value'] = life_value
self['attack'] = attack
__init__(*args,**kwargs)
return self
egg = Person('egon',78,10)
print(egg['name'])
面向對象小結——定義及調用的固定模式
class 類名:
def __init__(self,參數1,參數2):
self.對象的屬性1 = 參數1
self.對象的屬性2 = 參數2
def 方法名(self):pass
def 方法名2(self):pass
對象名 = 類名(1,2) #對象就是實例,代表一個具體的東西
#類名() : 類名+括弧就是實例化一個類,相當於調用了__init__方法
#括弧里傳參數,參數不需要傳self,其他與init中的形參一一對應
#結果返回一個對象
對象名.對象的屬性1 #查看對象的屬性,直接用 對象名.屬性名 即可
對象名.方法名() #調用類中的方法,直接用 對象名.方法名() 即可
練習一:在終端輸出如下信息
小明,10歲,男,上山去砍柴
小明,10歲,男,開車去東北
小明,10歲,男,最愛大保健
老李,90歲,男,上山去砍柴
老李,90歲,男,開車去東北
老李,90歲,男,最愛大保健
老張…
對象之間的交互
現在我們已經有一個人類了,通過給人類一些具體的屬性我們就可以拿到一個實實在在的人。
現在我們要再創建一個狗類,狗就不能打人了,只能咬人,所以我們給狗一個bite方法。
有了狗類,我們還要實例化一隻實實在在的狗出來。
然後人和狗就可以打架了。現在我們就來讓他們打一架吧!
創建一個狗類
class Dog: # 定義一個狗類
role = 'dog' # 狗的角色屬性都是狗
def __init__(self, name, breed, aggressivity, life_value):
self.name = name # 每一隻狗都有自己的昵稱;
self.breed = breed # 每一隻狗都有自己的品種;
self.aggressivity = aggressivity # 每一隻狗都有自己的攻擊力;
self.life_value = life_value # 每一隻狗都有自己的生命值;
def bite(self,people):
# 狗可以咬人,這裡的狗也是一個對象。
# 狗咬人,那麼人的生命值就會根據狗的攻擊力而下降
dog.life_value -= self.aggressivit
實例化一隻實實在在的二哈
ha2 = Dog('二愣子','哈士奇',10,1000) #創造了一隻實實在在的狗ha2
交互 egon打ha2一下
print(ha2.life_value) #看看ha2的生命值
egg.attack(ha2) #egg打了ha2一下
print(ha2.life_value) #ha2掉了10點血
完整的代碼
class Person: # 定義一個人類 role = 'person' # 人的角色屬性都是人 def __init__(self, name, aggressivity, life_value): self.name = name # 每一個角色都有自己的昵稱; self.aggressivity = aggressivity # 每一個角色都有自己的攻擊力; self.life_value = life_value # 每一個角色都有自己的生命值; def attack(self,dog): # 人可以攻擊狗,這裡的狗也是一個對象。 # 人攻擊狗,那麼狗的生命值就會根據人的攻擊力而下降 dog.life_value -= self.aggressivity class Dog: # 定義一個狗類 role = 'dog' # 狗的角色屬性都是狗 def __init__(self, name, breed, aggressivity, life_value): self.name = name # 每一隻狗都有自己的昵稱; self.breed = breed # 每一隻狗都有自己的品種; self.aggressivity = aggressivity # 每一隻狗都有自己的攻擊力; self.life_value = life_value # 每一隻狗都有自己的生命值; def bite(self,people): # 狗可以咬人,這裡的狗也是一個對象。 # 狗咬人,那麼人的生命值就會根據狗的攻擊力而下降 people.life_value -= self.aggressivity egg = Person('egon',10,1000) #創造了一個實實在在的人egg ha2 = Dog('二愣子','哈士奇',10,1000) #創造了一隻實實在在的狗ha2 print(ha2.life_value) #看看ha2的生命值 egg.attack(ha2) #egg打了ha2一下 print(ha2.life_value) #ha2掉了10點血egon大戰哈士奇
from math import pi class Circle: ''' 定義了一個圓形類; 提供計算面積(area)和周長(perimeter)的方法 ''' def __init__(self,radius): self.radius = radius def area(self): return pi * self.radius * self.radius def perimeter(self): return 2 * pi *self.radius circle = Circle(10) #實例化一個圓 area1 = circle.area() #計算圓面積 per1 = circle.perimeter() #計算圓周長 print(area1,per1) #列印圓面積和周長一個簡單的例子幫你理解面向對象
類命名空間與對象、實例的命名空間
創建一個類就會創建一個類的名稱空間,用來存儲類中定義的所有名字,這些名字稱為類的屬性
而類有兩種屬性:靜態屬性和動態屬性
- 靜態屬性就是直接在類中定義的變數
- 動態屬性就是定義在類中的方法
類的數據屬性是共用給所有對象的
>>>id(egg.role) 4341594072 >>>id(Person.role) 4341594072
而類的動態屬性是綁定到所有對象的
>>>egg.attack <bound method Person.attack of <__main__.Person object at 0x101285860>> >>>Person.attack <function Person.attack at 0x10127abf8>
創建一個對象/實例就會創建一個對象/實例的名稱空間,存放對象/實例的名字,稱為對象/實例的屬性
在obj.name會先從obj自己的名稱空間里找name,找不到則去類中找,類也找不到就找父類...最後都找不到就拋出異常
面向對象的組合用法
軟體重用的重要方式除了繼承之外還有另外一種方式,即:組合
組合指的是,在一個類中以另外一個類的對象作為數據屬性,稱為類的組合
class Weapon:
def prick(self, obj): # 這是該裝備的主動技能,扎死對方
obj.life_value -= 500 # 假設攻擊力是500
class Person: # 定義一個人類
role = 'person' # 人的角色屬性都是人
def __init__(self, name):
self.name = name # 每一個角色都有自己的昵稱;
self.weapon = Weapon() # 給角色綁定一個武器;
egg = Person('egon')
egg.weapon.prick()
#egg組合了一個武器的對象,可以直接egg.weapon來使用組合類中的所有方法
圓環是由兩個圓組成的,圓環的面積是外面圓的面積減去內部圓的面積。圓環的周長是內部圓的周長加上外部圓的周長。
這個時候,我們就首先實現一個圓形類,計算一個圓的周長和麵積。然後在"環形類"中組合圓形的實例作為自己的屬性來用
from math import pi
class Circle:
'''
定義了一個圓形類;
提供計算面積(area)和周長(perimeter)的方法
'''
def __init__(self,radius):
self.radius = radius
def area(self):
return pi * self.radius * self.radius
def perimeter(self):
return 2 * pi *self.radius
circle = Circle(10) #實例化一個圓
area1 = circle.area() #計算圓面積
per1 = circle.perimeter() #計算圓周長
print(area1,per1) #列印圓面積和周長
class Ring:
'''
定義了一個圓環類
提供圓環的面積和周長的方法
'''
def __init__(self,radius_outside,radius_inside):
self.outsid_circle = Circle(radius_outside)
self.inside_circle = Circle(radius_inside)
def area(self):
return self.outsid_circle.area() - self.inside_circle.area()
def perimeter(self):
return self.outsid_circle.perimeter() + self.inside_circle.perimeter()
ring = Ring(10,5) #實例化一個環形
print(ring.perimeter()) #計算環形的周長
print(ring.area()) #計算環形的面積
用組合的方式建立了類與組合的類之間的關係,它是一種‘有’的關係,比如教授有生日,教授教python課程
class BirthDate:
def __init__(self,year,month,day):
self.year=year
self.month=month
self.day=day
class Couse:
def __init__(self,name,price,period):
self.name=name
self.price=price
self.period=period
class Teacher:
def __init__(self,name,gender,birth,course):
self.name=name
self.gender=gender
self.birth=birth
self.course=course
def teach(self):
print('teaching')
p1=Teacher('egon','male',
BirthDate('1995','1','27'),
Couse('python','28000','4 months')
)
print(p1.birth.year,p1.birth.month,p1.birth.day)
print(p1.course.name,p1.course.price,p1.course.period)
'''
運行結果:
1 27
python 28000 4 months
'''
當類之間有顯著不同,並且較小的類是較大的類所需要的組件時,用組合比較好
初識面向對象小結
定義一個人類
class Person: # 定義一個人類
role = 'person' # 人的角色屬性都是人
def __init__(self, name, aggressivity, life_value, money):
self.name = name # 每一個角色都有自己的昵稱;
self.aggressivity = aggressivity # 每一個角色都有自己的攻擊力;
self.life_value = life_value # 每一個角色都有自己的生命值;
self.money = money
def attack(self,dog):
# 人可以攻擊狗,這裡的狗也是一個對象。
# 人攻擊狗,那麼狗的生命值就會根據人的攻擊力而下降
dog.life_value -= self.aggressivity
定義一個狗類
class Dog: # 定義一個狗類
role = 'dog' # 狗的角色屬性都是狗
def __init__(self, name, breed, aggressivity, life_value):
self.name = name # 每一隻狗都有自己的昵稱;
self.breed = breed # 每一隻狗都有自己的品種;
self.aggressivity = aggressivity # 每一隻狗都有自己的攻擊力;
self.life_value = life_value # 每一隻狗都有自己的生命值;
def bite(self,people):
# 狗可以咬人,這裡的狗也是一個對象。
# 狗咬人,那麼人的生命值就會根據狗的攻擊力而下降
people.life_value -= self.aggressivity
接下來,又創建一個新的兵器類。
class Weapon:
def __init__(self,name, price, aggrev, life_value):
self.name = name
self.price = price
self.aggrev = aggrev
self.life_value = life_value
def update(self, obj): #obj就是要帶這個裝備的人
obj.money -= self.price # 用這個武器的人花錢買所以對應的錢要減少
obj.aggressivity += self.aggrev # 帶上這個裝備可以讓人增加攻擊
obj.life_value += self.life_value # 帶上這個裝備可以讓人增加生命值
def prick(self, obj): # 這是該裝備的主動技能,扎死對方
obj.life_value -= 500 # 假設攻擊力是500
測試交互
lance = Weapon('長矛',200,6,100)
egg = Person('egon',10,1000,600) #創造了一個實實在在的人egg
ha2 = Dog('二愣子','哈士奇',10,1000) #創造了一隻實實在在的狗ha2
#egg獨自力戰"二愣子"深感吃力,決定窮畢生積蓄買一把武器
if egg.money > lance.price: #如果egg的錢比裝備的價格多,可以買一把長矛
lance.update(egg) #egg花錢買了一個長矛防身,且自身屬性得到了提高
egg.weapon = lance #egg裝備上了長矛
print(egg.money,egg.life_value,egg.aggressivity)
print(ha2.life_value)
egg.attack(ha2) #egg打了ha2一下
print(ha2.life_value)
egg.weapon.prick(ha2) #發動武器技能
print(ha2.life_value) #ha2不敵狡猾的人類用武器取勝,血槽空了一半
按照這種思路一點一點的設計類和對象,最終你完全可以實現一個對戰類游戲。
使用types模塊確認方法和函數的區別
使用pickle存取自定義類的對象的