Python基礎學習筆記(23)繼承 類部分屬性的補充 方法和函式 利用 pickle 儲存物件
Python基礎學習(23)繼承 類部分屬性的補充 方法和函式 利用 pickle 儲存物件
一、今日大綱
- 繼承
- 類部分屬性的補充
- 方法和函式
- 利用 pickle 儲存物件
二、繼承的基本實現
面向物件有三大特性:繼承、封裝、多型;今天我們主要介紹繼承;假如我們要定義一個貓類和一個狗類,他們各自具有下面的繫結方法:
-
貓:吃、喝、睡、爬樹
-
狗:吃、喝、睡、看家
class Cat: def __init__(self, name): self.name = name def eat(self): print(f'{self.name} is eating.') def drink(self): print(f'{self.name} is drinking.') def sleep(self): print(f'{self.name} is sleeping.') def climb_tree(self): print(f'{self.name} is climbing the tree.') class Dog: def __init__(self, name): self.name = name def eat(self): print(f'{self.name} is eating.') def drink(self): print(f'{self.name} is drinking.') def sleep(self): print(f'{self.name} is sleeping.') def guard_house(self): print(f'{self.name} is guarding the house.') xiaobai = Cat('xiaobai') xiaobai.eat() xiaobai.drink() xiaobai.climb_tree() xiaohei = Dog('xiaohei') xiaohei.eat() xiaohei.drink() xiaohei.guard_house()
根據上面的程式碼我們可以看到,由於貓狗兩個類由於大部分繫結方法比較相似,所以存在大量的重複程式碼,所以我們引入一個概念:繼承;它主要用於解決程式碼的重複問題,下面是繼承的基本實現方式:
class A:
pass
class B(A):
pass
# B繼承A,A是父類,B是子類
# A是父類 基類 超類
# B是子類 派生類
而根據繼承的思想,我們可以新定義一個父類:Pet
,讓Cat
和Dog
都繼承此類,從而達到解決程式碼重複問題的作用:
class Pet: def __init__(self, name): self.name = name def eat(self): print(f'{self.name} is eating.') def drink(self): print(f'{self.name} is drinking.') def sleep(self): print(f'{self.name} is sleeping.') class Dog(Pet): def guard_house(self): print(f'{self.name} is guarding the house.') class Cat(Pet): def climb_tree(self): print(f'{self.name} is climbing the tree.') xiaobai = Cat('xiaobai') xiaobai.eat() xiaohei = Dog('xiaohei') xiaohei.eat()
物件尋找方法的基本邏輯:在定義物件時,先開闢空間,空間裡存放一個類指標指向類;當呼叫某些方法時,物件會首先在自己的名稱空間中尋找,如果沒找到會藉助類指標去類的名稱空間中尋找,如果還未找到,則會藉助類指標去父類中進行尋找。
如果我們這時想給貓和狗類定義一個新的方法:吃貓糧/狗糧
class Pet: def __init__(self, name): self.name = name def eat(self): print(f'{self.name} is eating.') def drink(self): print(f'{self.name} is drinking.') def sleep(self): print(f'{self.name} is sleeping.') class Dog(Pet): def guard_house(self): print(f'{self.name} is guarding the house.') def eat(self): print(f'{self.name}吃狗糧。') class Cat(Pet): def climb_tree(self): print(f'{self.name} is climbing the tree.') def eat(self): print(f'{self.name}吃貓糧。') # 這時在重新呼叫小白和小黑的吃方法 xiaobai = Cat('xiaobai') xiaobai.eat() # xiaobai吃貓糧。 xiaohei = Dog('xiaohei') xiaohei.eat() # xiaohei吃狗糧。
這時我們發現,程式碼又出現了重複,我們是否可以利用傳參實現把吃貓糧和吃狗糧合併稱為一個方法呢?我們新定義兩個屬性來更直觀地觀察實現方法:智力值、血量;貓吃了貓糧智力值會增加,狗吃了狗糧生命值會增加。
class Pet:
def __init__(self, name, food):
self.name = name
self.food = food
self.blood = 100
self.wise = 100
def eat(self):
print(f'{self.name} is eating {self.food}.')
def drink(self):
print(f'{self.name} is drinking.')
def sleep(self):
print(f'{self.name} is sleeping.')
class Dog(Pet):
def eat(self):
self.blood += 100
Pet.eat(self) # 手動呼叫父類的方法,由於父類方法沒有例項化,必須吧self傳入
def guard_house(self):
print(f'{self.name} is guarding the house.')
class Cat(Pet):
def eat(self):
self.wise += 100
Pet.eat(self) # # 手動呼叫父類的方法,由於父類方法沒有例項化,必須吧self傳入
def climb_tree(self):
print(f'{self.name} is climbing the tree.')
xiaobai = Cat('xiaobai', '貓糧')
xiaobai.eat() # xiaobai吃貓糧。
xiaohei = Dog('xiaohei', '狗糧')
xiaohei.eat() # xiaohei吃狗糧。
print(xiaobai.wise, xiaohei.blood) # 200 200
在子類和父類方法存在重名的時候:子類物件呼叫方法會按照子類 -> 父類的順序檢查,先檢查到的優先呼叫。而如果想同時呼叫子類和父類就要在子類中手動呼叫父類的方法,即新增Classname.func(self)
。
# 思考題1:
class Father:
def __init__(self):
self.func()
def func(self):
print('in father')
class Son(Father):
def func(self):
print('in son')
Father.func(self)
Son()
# in son
# in father
# 思考題2:想給貓和狗定製個性屬性
# 貓有eye_color眼睛的顏色
# 狗有size大小
class Pet:
def __init__(self, name, food):
self.name = name
self.food = food
self.blood = 100
self.wise = 100
def eat(self):
print(f'{self.name} is eating {self.food}.')
def drink(self):
print(f'{self.name} is drinking.')
def sleep(self):
print(f'{self.name} is sleeping.')
class Dog(Pet):
def __init__(self, name, food, size):
Pet.__init__(self, name, food)
self.size = size
def eat(self):
self.blood += 100
Pet.eat(self) # 手動呼叫父類的方法,由於父類方法沒有例項化,必須吧self傳入
def guard_house(self):
print(f'{self.name} is guarding the house.')
class Cat(Pet):
def __init__(self, name, food, eye_color):
Pet.__init__(self, name, food) # 呼叫了父類的初始化,去完成一些通用屬性的初始化
self.eye_color = eye_color # 派生屬性
def eat(self):
self.blood += 100
Pet.eat(self)
def climb_tree(self):
print(f'{self.name} is climbing the tree.')
xiaobai = Cat('xiaobai', 'maoliang', 'blue')
xiaohei = Dog('xiaohei', 'gouliang', 'big')
print(xiaobai.__dict__)
print(xiaohei.__dict__)
# {'name': 'xiaobai', 'food': 'maoliang', 'blood': 100, 'wise': 100, 'eye_color': 'blue'}
# {'name': 'xiaohei', 'food': 'gouliang', 'blood': 100, 'wise': 100, 'size': 'big'}
繼承主要分為單繼承和多繼承:單繼承只繼承一個類;而多繼承可以繼承多個類;多繼承的呼叫邏輯如下:
# 單繼承: 只繼承一個類
# 調子類的:子類自己有的時候
# 調父類的:子類自己沒有的時候
# 調子類和父類的:子類父類都有,在子類中呼叫父類的
class A:
def func(self): print('in A')
class B(A): pass
class C(B): pass
class D(C): pass
d = D()
d.func() # in A
# 多繼承:繼承多個類
# 按照繼承順序,先繼承的先尋找,找到就停止
# 有一些語言不支援多繼承 java
# python語言的特點:可以在面向物件中支援多繼承
class A:
def func(self): print('in A.')
class B:
def func(self): print('in B.')
class C(A, B): pass
C().func() # 繼承第一個傳入的類
三、類部分屬性的補充
-
object
類所有在 Python3x 中的類,都是繼承 object 類的。一般按照引用的邏輯來講,我們定義一個類中未定義
__init__
的物件,發現並不會報錯,這就是因為物件的類中其實是繼承了object
類的,所以我們以後在定義類的時候最好還是把object
寫出來,雖然實際上可以省略,但是這是一個約定的寫法,即:class A: pass # 等價於 class A(object): pass class B: pass class C(A, B): pass print(C.__bases__) # (<class '__main__.A'>, <class '__main__.B'>) print(B.__bases__) # (<class 'object'>,)
-
類屬性的補充
# 類屬性的補充 # class_name.__name__ # 類的名字 # class_name.__doc__ # 類的文件字串 # class_name.__base__ # 類的第一個父類 # class_name.__bases__ # 類的所有父類構成的元組 # class_name.__dict__ # 類的字典屬性 # class_name.__module__ # 類定義所在的模組 # object_name.__class__ # 例項對應的類 class A: pass class B: """ 這個類主要是用來賣萌 """ pass class C(A, B): pass print(A.__base__) # <class 'object'> print(B.__base__) # <class 'object'> print(C.__bases__) # (<class '__main__.A'>, <class '__main__.B'>) print(C.__class__) # <class 'type'> print(C.__module__) # __main__ print(B.__doc__) # 這個類主要是用來賣萌
四、函式和方法
# 繫結方法和普通的函式
from types import FunctionType, MethodType
# FunctionType: 函式
# MethodType: 方法 是對一個物件進行的操作
class A:
def func(self):
print('in func')
print(A.func) # 函式 <function A.func at 0x0000019D33A1BD90>
a = A()
print(a.func) # 方法 <bound method A.func of <__main__.A object at 0x0000019D33A1F780>>
print(isinstance(a.func, MethodType)) # True
print(isinstance(a.func, FunctionType)) # False
print(isinstance(A.func, MethodType)) # False
print(isinstance(A.func, FunctionType)) # True
# isinstance 和 type
# isinstance和type大部分時間用法是比較類似的
# 但是isinstance可以鑑別物件的類的父類,而type是不可以的
a = 1
b = 'abc'
print(isinstance(a, int)) # True
print(isinstance(a, float)) # False
print(isinstance(b, str)) # True
print(type(a) is int) # True
print(type(b) is str) # True
class Cat:
pass
xiaobai = Cat()
print(type(xiaobai) is Cat) # True
print(isinstance(xiaobai, Cat)) # True
class Animal: pass
class Cat(Animal): pass
xiaobai = Cat()
print(type(xiaobai) is Cat) # True
print(type(xiaobai) is Animal) # False
print(isinstance(xiaobai, Cat)) # True
print(isinstance(xiaobai, Animal)) # True
五、利用 pickle 儲存物件
class Course:
def __init__(self, name, period, price):
self.name = name
self.period = period
self.price = price
python = Course('python', '6 month', 21800)
linux = Course('python', '5 month', 19800)
go = Course('python', '4 month', 12800)
import pickle
with open('pickle_file', 'ab') as f:
pickle.dump(python, f)
pickle.dump(linux, f)
pickle.dump(go, f)
with open('pickle_file', 'rb') as f:
while True:
try:
ret = pickle.load(f)
print(ret.__dict__)
except EOFError:
break
# {'name': 'python', 'period': '6 month', 'price': 21800}
# {'name': 'python', 'period': '5 month', 'price': 19800}
# {'name': 'python', 'period': '4 month', 'price': 12800}
# 在遊戲中儲存物件
# json不可以連續load連續dump
# json不可以dump和load物件(模組中沒有類的宣告,讀取過程中會出現報錯)
# AttributeError: Can't get attribute 'Course' on <module '__main__' from 'D:/Python/Python_Project/day24/03 pickle用法.py'>