第二十七篇 類和對象相關知識
類和對象
1. 什麽叫類:類是一種數據結構,就好比一個模型,該模型用來表述一類食物(食物即數據和動作的結合體),用它來生產真是的物體(實例)
2. 什麽叫對象:睜開眼,你看到的一切事物都是一個個的對象,你可以把對象理解為一個具體的事物(事物即數據和動作的結合體)
(鉛筆是對象,人是對象,房子是對象,狗是對象,你看到的都是類)
3.類與對象的關系:對象都是由類產生的,女媧造人,首先由一個造人的模板,這個模板就是人類,然後女媧根據類的定義來生產一個個的人
4. 什麽叫實例化:由類生產對象的過程叫實例化,類實例化的結構就是一個對象,或者叫做一個實例(實例=對象)
實例:就是類生產的那個對象。是一個實實在在的存在。放到真實世界中,人類就是一個類,這人類是看不見,摸不著的,是人為給劃分的;而人,比如,你,我,他,路人等就是一個個的實例,是真實存在,能看得見,摸得著的。
類相關的知識
-
聲明類
在Python中,聲明函數和聲明類很相似:
聲明函數
def function(args): ‘‘‘文檔字符串‘‘‘ 函數體
聲明類
class 類名: ‘‘‘類的文檔字符串‘‘‘ 類體
class Chinese: # 聲明類,類名的規範:類名的首字母要大寫 ‘‘‘這是一個中國人的類‘‘‘ pass print(Chinese) # <class ‘__main__.Chinese‘>聲明類的定義示例
class Chinese: ‘‘‘這是一個中國人的類‘‘‘類的實例化pass # 實例化 #實例化,對類名加括號,加括號就代表運行,運行類也有返回值,這個返回值就是一個具體的示例了 p1=Chinese() print(p1) # <__main__.Chinese object at 0x00402EF0> #實例化到底幹了什麽?後面給你答案
經典類與新式類
經典類:上面的兩個示例就是經典類,經典類的特點就是,類名後面直接就是冒號(Python2中)
新式類:都要在類名後面加個括號,括號裏寫上object
# 新式類:都要在類名後面加個括號,括號裏寫上object # 意思是Chinese這類繼承與object class Chinese(object):pass
註意,Python3中統一都是新式類,沒有區別了,類名後面是否加(object),都是新式類。
-
屬性
類是用來描述一類事物的,類的對象指的是這一類事物中的一個個體。
既然是事物,那就要有屬性,屬性分為:
1. 數據屬性:就是變量 (就是第二十五篇裏講的特征)
2. 函數屬性:就是函數,在面向對象裏通常稱為方法 (就是第二十五篇裏講的動作)
註意:
類和對象均用點來訪問自己的屬性
-
類的屬性
數據屬性即變量,類的定義與函數又極其類似,其實可以用函數的作用域來理解類的屬性調用。
class Chinese: ‘這是一個中國人的類‘ # 類的數據屬性 party=‘渣滓洞‘ # 類的函數屬性,就是方法 def huang_pi_fu(): print(‘中國人都是黃皮膚‘) def cha_dui(self): print(‘%s插到了前面‘ %self)
-
訪問類的屬性(查看類屬性)
要訪問類的屬性,你首先得知道類的屬性在哪裏吧?就好比你要逛親戚,你得知道親戚家在哪裏吧,然後才能選擇用什麽交通工具到你親戚家。
那要去你親戚家,你怎麽知道路呢?可以問爸媽,也可以給親戚打電話,還可以讓親戚發定位等等。
同理,定義了類的屬性,也要知道存放在哪裏了?有兩種方式可以查看
#第一種: dir(類名): 得到的是一個名字列表,只放了屬性的名字在列表裏 print(dir(Chinese)) # [‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘, ‘cha_dui‘, ‘huang_pi_fu‘, ‘party‘] # 第二種: 類名.__dict__ :查出的是一個字典,key為屬性名,value為屬性值
__dict__: 查看類的屬性字典
print(Chinese.__dict__) # {‘__module__‘: ‘__main__‘, ‘__doc__‘: ‘這是一個中國人的類‘, ‘party‘: ‘渣滓洞‘, ‘huang_pi_fu‘: <function Chinese.huang_pi_fu at 0x00C01AE0>, ‘cha_dui‘: <function Chinese.cha_dui at 0x00C01A50>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>}
知道了存放地點,就可以一級級查找下去,訪問類的屬性啦
print(Chinese.__dict__[‘party‘]) # 渣滓洞 print(Chinese.__dict__[‘huang_pi_fu‘]()) # 中國人都是黃皮膚 print(Chinese.__dict__[‘cha_dui‘](‘汪汪隊‘)) # 汪汪隊插到了前面
Python提供了更簡單的方法:
類名.屬性名:就可以訪問到屬性。(本質上就是在查詢屬性字典)
# 訪問類的數據屬性,通過點來訪問 print(Chinese.party) # 渣滓洞 # 訪問類的函數屬性,通過點來訪問 Chinese.huang_pi_fu() # 中國人都是黃皮膚 Chinese.cha_dui(‘dig‘) # dig插到了前面
-
特殊的類屬性
#python為類內置的特殊屬性 類名.__name__# 類的名字(字符串) 類名.__doc__# 類的文檔字符串 類名.__base__# 類的第一個父類(在講繼承時會講) 類名.__bases__# 類所有父類構成的元組(在講繼承時會講) 類名.__dict__# 類的字典屬性 類名.__module__# 類定義所在的模塊 類名.__class__# 實例對應的類(僅新式類中) 類的特殊屬性(了解即可)
print(Chinese.__name__) # Chinese print(Chinese.__doc__) # 這是一個中國人的類 print(Chinese.__base__) # <class ‘object‘> print(Chinese.__bases__) # (<class ‘object‘>,) print(Chinese.__dict__) # 上面已經有了 print(Chinese.__module__) #__main__ print(Chinese.__class__) #<class ‘type‘>
對象相關的知識
對象:是由類實例化而來的。
- 構造初始化方法及實例化
class Chinese: ‘這是一個中國人的類‘ party=‘渣滓洞‘ ‘‘‘ 首先,在類定義當中,要有一個初始化函數來幫我們定制每一個對象的屬性 其次,類class的語法結構當中,提供了內置方法,只要你把初始化函數定義為__init__()這個名字,你在用類名()來運行你的類的時候,他就會自動的找到 __init__來幫你去運行 第三,__init__()必須要有一個self參數,然後再接著寫其他數據屬性(變量參數) 第四, 定義數據屬性時,self.mingzi = name,代表給這個self這個實例賦予了一個mignzi屬性,mingzi屬性就是數據屬性;name就是執行__init()函數傳進來的參數 這樣就把名字,年齡,性別統統都封裝到了self這個實例自己裏面了 最後,return返回了一個self實例,但是不用顯示的return,而是class自動給你return了,最後返回的結果就是一個字典。 字典裏封裝了什麽?封裝了名字,年齡,性別這些數據屬性 ‘‘‘
# 1. 初始化構造方法 def __init__(self,name,age,gender): print(‘我是初始化函數,我開始運行了‘) # self:就是實例自己 self.mingzi=name # 相當於 p1.mingzi=name self.nianji=age # 相當於 p1.nianji=age self.xingbie=gender # 相當於 p1.xingbie = gender print(‘我結束啦‘) # 上面已經構造好了初始化一個實例的方法,下面就來真真的生成一個實例對象 # 具體的實例化的過程: # 1. 實例化的過程,本質上就是調用並運行了一次__init__(self,name,age,gender)函數, # 2. 在調用__init__的過程中,self其實就是p1,在運行過程中,會自動的將p1傳給self,然後‘元昊‘,18,‘female‘分別傳給name,age,gender # 3. 其實就是Chinese.__init__(p1,name,age,gender)
# 2. 實例化:實際就是生成了一個真實存在的對象。
p1 = Chinese(‘小黃‘,18,‘female‘)
# 我是初始化函數,我開始運行了
# 我結束啦
# 上面初始化構造方法裏面已經講了,初始化構造返回的實例就是一個數據字典,那我們來看看到底是不是 print(p1) # <__main__.Chinese object at 0x00C39890> print(p1.__dict__) # {‘mingzi‘: ‘元昊‘, ‘nianji‘: 18, ‘xingbie‘: ‘female‘}
- 實例調用屬性
# 上面已經實例化了一個p1,p1就是真實存在的一個人 # 就可以查看p1這個人具備哪些屬性? # 首先,他具有這個類所共有的屬性,還可以有他自己特有的屬性 print(p1.mingzi) # 小黃 # p1.mingzi,作用域在__init__裏,可以調用到,很正常 # 那p1.dang首先在__init__作用域裏沒找到,就會向外一層找,就有dang這個屬性啦,所以可以調用了 print(p1.dang) # 渣滓洞 # 查看p1的屬性 print(p1.__dict__) # {‘mingzi‘: ‘元昊‘, ‘nianji‘: 18, ‘xingbie‘: ‘female‘} # 查看類的屬性 print(Chinese.__dict__) # {‘__module__‘: ‘__main__‘, ‘__doc__‘: ‘這是一個中國人的類‘, ‘dang‘: ‘渣滓洞‘, ‘__init__‘: <function Chinese.__init__ at 0x00AB1B70>, ‘sui_di_tu_tan‘: <function Chinese.sui_di_tu_tan at 0x00AB1B28>, ‘cha_dui‘: <function Chinese.cha_dui at 0x00AB1AE0>, ‘eat_food‘: <function Chinese.eat_food at 0x00AB1A50>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>}
小總結:
1. 實例是怎麽產生的?
【答】實例的產生,就是執行了一個初始化方法__init__()產生的。__init__()返回的就是一個實例屬性字典。實例屬性字典裏就是沒有函數屬性,並不包含函數屬性
2. 函數屬性是屬於類的,從層級關系上也能看出來。
- 構造函數屬性
class Chinese: ‘這是一個中國人的類‘ dang=‘渣滓洞‘ # 構造了初始化函數 def __init__(self,name,age,gender): self.mingzi=name # 相當於 p1.mingzi=name self.nianji=age # 相當於 p1.nianji=age self.xingbie=gender # 相當於 p1.xingbie = gender # 構造函數屬性 def sui_di_tu_tan(self): print(‘%s 朝著墻上就是一口痰‘ %self.mingzi) def cha_dui(self): print(self) print(‘%s 插到了前面‘ %self.mingzi) def eat_food(self,food): print(‘%s 正在吃%s‘ %(self.mingzi,food))
- 實例調用函數屬性
# 實例調方法的順序 # 1. 先從自己的字典裏找 p1自己的字典:{‘mingzi‘: ‘元昊‘, ‘nianji‘: 18, ‘xingbie‘: ‘female‘} # 2. 再到類的字典裏面去找, 類的字典:Chinese.__dict__ # 能找到就可以運行 p1.sui_di_tu_tan() # 小黃 朝著墻上就是一口痰 # 問題:sui_di_tu_tan(self)定義的時候有個self參數,那為什麽這裏運行的時候,沒有傳參數,也能正常運行呢?
- 關於self的總結
1、self 就代表自己,自己就是實例化的結果p1。誰來實例化,self就是誰。
2、為什麽要有self?self就是來做統一的
3、只要定義了self,一執行函數,就會自動把p1傳給函數的第一個參數。
4、所以,以後只要碰到self,就要知道self就是實例本身
來一張圖
有人說,類有數據屬性和函數屬性,實例/對象 是由類產生的,所以實例也有數據屬性和函數屬性了------這是錯的哇,記住啦。
因為:
實例化的過程實際就是執行__init__的過程,這個__init__函數內部只是為實例本身即self設定了一堆數據(變量),所以實例只有數據屬性;它的所謂的函數屬性是從類裏找來的而已。
還有人說,實例是類產生的,所以實例肯定能訪問到類屬性,然後就沒有說為什麽了------這也是錯的哇。
因為:
1. 首先你會發現,實例化就是 類名(),然後返回的結果是一個對象,加上括號是不是跟函數運行很像,函數運行完了有返回值,很像那
2. 函數有作用域的概念,其實類也有作用域的概念,二者一樣
3. 你可以吧class當做最外層的函數,是一個作用域
# 定義一個類,只當一個作用域用 class MyData: pass x = 10 y = 20 MyData.x = 1 MyData.y = 2 print(x, y) print(MyData.x, MyData.y) print(MyData.x + MyData.y)View Code
4. 實例化會自動觸發__init__函數的運行,最後返回一個值即實例,我們要找的實例屬性就存放在__init__函數的局部作用域裏
5. 類有類的屬性字典,就是類的作用域,實例有實例的屬性字典,就是實例的作用域
6. 綜上,一個點代表一層作用域,obj.x 先從自己的作用域找,自己找不到再去外出的類的字典中找,都找不到,就會報錯
7. 在類中沒有使用點的調用,代表調用全局變量。
所以,上面的說法犯了因果倒置的錯誤。不是因為實例是由類產生的,所以實例才能訪問到類屬性。
上面這個只是為了幫助理解,而不能真的這麽說。
茅塞頓開,總結一句話就是:
類有個屬性字典,實例也有個屬性字典;
查的時候,實例/對象先從自己的屬性字典裏查,再到類的屬性字典裏去查
整理下上面的代碼
class Chinese: ‘這是一個中國人的類‘ dang=‘渣滓洞‘ def __init__(self,name,age,gender): self.mingzi=name # 相當於 p1.mingzi=name self.nianji=age self.xingbie=gender def sui_di_tu_tan(self): print(‘%s 朝著墻上就是一口痰‘ %self.mingzi) def cha_dui(self): print(self) print(‘%s 插到了前面‘ %self.mingzi) def eat_food(self,food): print(‘%s 正在吃%s‘ %(self.mingzi,food)) # 實例化可以生成多個對象 p1=Chinese(‘小黃‘,18,‘female‘) # 對象調用吃的方法 p1.eat_food(‘包子‘) # 小黃 正在吃包子 p2=Chinese(‘武sir‘,23,‘姑娘‘) # 對象也可以調用吃的方法 p2.eat_food(‘韭菜餡餅‘) 武sir 正在吃韭菜餡餅
- 屬性有兩種:數據屬性和函數屬性
- 類屬性的使用----增刪改查
class Chinese: country=‘China‘ def __init__(self,name): self.name=name # 函數屬性的定義原則:動詞_名詞。 幹什麽事 def play_ball(self,ball): print(‘%s 正在打 %s‘ %(self.name,ball))
# 下面的函數定義與類是同級的,與類沒半毛錢關系。 def say_word(self,word): return "%s 說 %s" %(self.name, word) # 實例化一個實例 p1=Chinese(‘alex‘) # print(p1.__dict__) # {‘name‘: ‘alex‘}
-
- 查看類的屬性
#查看類的數據屬性:用點的方式查。 print(Chinese.country) # China # 查看類的函數屬性 print(Chinese.play_ball) # <function Chinese.play_ball at 0x009F1AE0>
-
- 增加類的屬性
#增加類的數據屬性 Chinese.dang = ‘Gong Chandang‘ print(Chinese.dang) # Gong Chandang # 增加類的函數屬性 # 首先,要在類的外部寫一個函數 def say_word(self,word): return "%s 說 %s" %(self.name, word) # 其次,增加函數屬性,將上面的函數賦值給類的函數屬性Say_Language Chinese.Say_Language = say_word print(Chinese.__dict__) # {‘__module__‘: ‘__main__‘, ‘country‘: ‘China‘, ‘__init__‘: <function Chinese.__init__ at 0x00C01B28>, ‘play_ball‘: <function Chinese.play_ball at 0x00C01AE0>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>, ‘__doc__‘: None, ‘Say_Language‘: <function say_word at 0x00C01B70>}
- 修改類的屬性
#修改類的數據屬性 Chinese.country="新西蘭" print(Chinese.country) # 新西蘭 # 類屬性被修改了,那實例可以使用嗎?可以用的 print(p1.country) # 新西蘭 # 修改類的函數屬性 # 首先要定義個要修改的目標函數 def play_what(self,what): return "%s 正在玩 %s" %(self.name, what) # 修改類的函數屬性 Chinese.play_ball = play_what print(Chinese.__dict__) # 調用類的函數屬性 print(p1.play_ball("鋼琴")) # alex 正在玩 鋼琴
-
- 刪除類的屬性
# 刪除類的數據屬性 del Chinese.country print(p1.country) # AttributeError: ‘Chinese‘ object has no attribute ‘country‘ # 刪除類的函數屬性 del Chinese.play_ball print(Chinese.__dict__) # {‘__module__‘: ‘__main__‘, ‘country‘: ‘China‘, ‘__init__‘: <function Chinese.__init__ at 0x01331B28>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>, ‘__doc__‘: None} # 調用類函數屬性 p1.play_ball("足球") # AttributeError: ‘Chinese‘ object has no attribute ‘play_ball‘ # 因為,已經被刪除掉了
- 實例屬性的使用----增刪改查
- 查看實例的屬性
# 查看實例的數據屬性 print(p1.name) # alex # 查看實例類屬性(查看實例的函數屬性,實際上是訪問的類的函數屬性) print(p1.play_ball) # <bound method Chinese.play_ball of <__main__.Chinese object at 0x00D22ED0>>
# 運行,函數屬性後加()就變成運行了。
print(p1.play_ball(‘籃球‘))
-
- 增加實例的屬性
# 增加實例的數據屬性 p1.age=18 print(p1.__dict__) # {‘name‘: ‘alex‘, ‘age‘: 18} print(p1.age) # 18 #備註,無法增加實例的函數屬性哦,因為函數屬性是通過類增加的 # 增加實例的函數屬性(雖然可以增加,但是沒有這麽玩的,要知道可以增加,但要迅速忘記它,千萬別這麽用,純屬多余的) def shili_shuxing(self): return "我是來自實例的函數屬性" p1.Shi_Li_De_Shu_Xing = shili_shuxing print(p1.__dict__) # {‘name‘: ‘alex‘, ‘Shi_Li_De_Shu_Xing‘: <function shili_shuxing at 0x00D61AE0>} print(p1.Shi_Li_De_Shu_Xing(p1)) # 我是來自實例的函數屬性
- 修改實例的屬性
# 修改實例的數據屬性 p1.age=19 print(p1.__dict__) print(p1.age) # 結果 {‘name‘: ‘alex‘, ‘age‘: 19} 19
-
- 刪除實例的屬性
#刪除實例的數據屬性 del p1.age print(p1.__dict__)
那,換個姿勢,實例和類混合著玩會怎麽樣?
# 示例代碼 class Chinese: country=‘China‘ def __init__(self,name): self.name=name def play_ball(self,ball): print(‘%s 正在打 %s‘ %(self.name,ball)) p1=Chinese(‘alex‘) print(p1.country) # China 訪問的是類的 # 在p1裏新增了一個country p1.country=‘日本‘ print(‘類的--->‘,Chinese.country) # China print(‘實例的‘,p1.country) # 日本示例代碼1
# 作用域問題 class Chinese: def __init__(self,name): self.name=name def play_ball(self,ball): print(‘%s 正在打 %s‘ %(self.name,ball)) p1=Chinese(‘alex‘) print(p1.country) # AttributeError: ‘Chinese‘ object has no attribute ‘country‘ # 原因, 因為類裏沒有country屬性 # 那如果把country定義到類外面那? country=‘中國‘ class Chinese: def __init__(self,name): self.name=name def play_ball(self,ball): print(‘%s 正在打 %s‘ %(self.name,ball)) p1=Chinese(‘alex‘) print(p1.country) # AttributeError: ‘Chinese‘ object has no attribute ‘country‘ # 原因:作用域只在類裏面找,放到類外部照樣找不到示例代碼2
class Chinese: def __init__(self): # 在實例化過程中接受輸入參數 name = input("請輸入用戶名: ") self.name=name def play_ball(self,ball): print(‘%s 正在打 %s‘ %(self.name,ball)) p1=Chinese() print(p1.name) # 備註: ‘‘‘ 雖然這麽做是可以的,但是千萬別這麽幹。 因為一個函數就是實現一個功能,而這麽幹,還實現了接受用戶輸入的功能。 可讀性差,也不利於維護 ‘‘‘ # 可以改進成如下方式,專門寫一個函數來處理接受輸入和實例化的過程,代碼變為: class Chinese: def __init__(self, name): self.name=name def play_ball(self,ball): print(‘%s 正在打 %s‘ %(self.name,ball)) def shi_li_hua(): name=input(‘>>: ‘) # 接收輸入 p1=Chinese(name) # 實例化 print(p1.country) # 調用 print(p1.name) # 調用 shi_li_hua() # 直接調用運行千萬不要這麽幹
在類中沒有使用點的調用,代表調用全局變量。
country=‘中國‘ class Chinese: def __init__(self,name): self.name=name print(‘--->‘,country) def play_ball(self,ball): print(‘%s 正在打 %s‘ %(self.name,ball)) p1=Chinese(‘alex‘) # ---> 中國 # 為什麽? # 其實前面已經給了答案了。 所說的在類裏面找屬性,是通過點這種方式才表明這個屬性是需要再類裏面去找的,而此時,country並沒有通過點的方式,所以它就是一個普通的變量名,即使這個變量在類的外面,照樣也可以使用不通過點的方式也可以在類裏使用變量
country=‘-------------中國-------------‘ class Chinese: country=‘中國‘ def __init__(self,name): self.name=name print(‘--->‘,country) def play_ball(self,ball): print(‘%s 正在打 %s‘ %(self.name,ball)) p1 = Chinese("alex") # ---> -------------中國------------- # 原因: # 遵循在類中沒有使用點的調用,代表調用全局變量。遵循在類中沒有使用點的調用,代表調用全局變量。沒有通過點,根本不會從類裏面找的
class Chinese: country=‘China‘ def __init__(self,name): self.name=name def play_ball(self,ball): print(‘%s 正在打 %s‘ %(self.name,ball)) p1=Chinese(‘alex‘) print(p1.country) # China p1.country=‘Japan‘ # 改的只是p1自己的國家 print(Chinese.country) # China 類的數據屬性還是沒有被改掉的會改掉類的屬性嗎?
class Chinese: country=‘China‘ l=[‘a‘,‘b‘] def __init__(self,name): self.name=name def play_ball(self,ball): print(‘%s 正在打 %s‘ %(self.name,ball)) p1=Chinese(‘alex‘) print(p1.l) # [‘a‘, ‘b‘] p1.l=[1,2,3] # 給p1新增了一個屬性值,所以只會改p1自己的 print(p1.l) # [1, 2, 3] 改的僅僅是p1自己的 print(Chinese.l) # [‘a‘, ‘b‘] 類的屬性l 是不會變的 print(p1.__dict__) # {‘name‘: ‘alex‘, ‘l‘: [1, 2, 3]} # 但是,這種方式就有問題了 # 首先,沒有給p1新定義屬性, # 其次, p1.l調的就是類的,這個l那的就是列表的引用,append操作的就是類 p1.l.append(‘c‘) print(p1.__dict__) # {‘name‘: ‘alex‘, ‘l‘: [1, 2, 3]} print(Chinese.l) # {‘name‘: ‘alex‘, ‘l‘: [1, 2, 3, ‘c‘]} # 這其實就是通過實力引用類的屬性,並直接操作了類的屬性非賦值方式通過實例修改類的屬性
第二十七篇 類和對象相關知識