python
中可以在類的外面給對象增加屬性,比如tom.name="Tom"
就可以給tom
類增加一個name
屬性,但是這種方法並不推薦使用,最好都封裝在類的內部。
在類的內部封裝可以通過self
實現,self
的地址會跟實例化對象的地址一樣。
在python3
中object
是所有類的基類。
封裝
所謂封裝,就是將屬性和方法抽象地封裝到一個類中。
繼承和重寫
面向對象(OOP
)三要素(封裝 繼承 多態)之一,繼承Inheritance。繼承可以讓子類從父類獲取特徵(屬性和方法),在面向對象的世界中,從父類繼承,就可以直接擁有父類的屬性和方法。
它可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴展,這樣可以減少代碼,多複用,子類可以定義自己的屬性和方法。
在使用面向對象開發前,應該首先分析需求,確定一下,程序中需要包含哪些類。
繼承基本思想
在使用類創建一個對象的時候就會執行init
方法,如果子類實現了init
方法,會走子類中的init
方法,如果子類沒有實現init
方法,會找繼承父類init
方法。
class Envrioment:
def __init__(self,env):
self.env = env
def get_observation(self):
print("Get Observation")
class Big_Env(Envrioment):
pass
a = Big_Env(1)
a.get_observation()
print(a.env)
>>>Get Observation
1
繼承是具有傳遞性的,也就是子類也會有爺爺類的方法。
super()擴展父類,重寫
如果父類原本封裝的方法實現是子類方法的一部分,就可以使用擴展的方式。在需要的位置使用super().父類方法
來調用父類方法的執行,代碼其它的位置針對子類的需求編寫子類特有的代碼實現。
在python
中super()
是一個特殊的類,super()
就是使用super
類創建出來的對象,最常使用的場景就是在重寫父類方法時,調用在父類中封裝的方法實現。
子類同樣是不能訪問父類的私有屬性和私有方法的。
可依據魔法屬性mro
(method resolution order)順序查看。
class little_animal():
def __init__(self, sleep):
self.sleep = sleep
class animal(little_animal):
def __init__(self, sleep, eat, drink):
super().__init__(sleep)
self.eat = eat
self.drink = drink
def run(self):
print("rrrrr")
class dog(animal):
def __init__(self,bark,sleep,eat,drink):
super().__init__(sleep,eat,drink)
self.bark = bark
def run(self):
super().run()
print("ttttt")
a = dog(1,2,3,4)
print(a.bark, a.sleep, a.eat, a.drink)
a.run()
print(dog.__mro__)
輸出:
1 2 3 4
rrrrr
ttttt
(<class '__main__.dog'>, <class '__main__.animal'>, <class '__main__.little_animal'>, <class 'object'>)
在繼承裏面一定要注意的是繼承不是複製,他只是指向父類,此時如果改變父類的值,子類相應的值也會發生改變。只有重寫之後,也就是在子類中對其進行重新賦值之後,改變父類的值不會改變子類的值。
多繼承
子類可以有多個父類,並且具有所有父類的屬性和方法。
class A:
def test(self):
print("Test")
class B:
def demo(self):
print("demo")
class C(A,B):
pass
C_test = C()
C_test.test()
C_test.demo()
>>>Test
demo
在多繼承中要注意的問題就是,應該儘量避免產生不同父類存在同名的方法。
爲了保證編寫的代碼能夠同時在Python2
和Python3
中同時運行,今後定義類時,如果沒有父類,建議統一繼承自object
。
多態
多態:不同的子類對象調用相同的父類方法,產生相同的執行結果。多態可以增加代碼的靈活度,以繼承和重寫父類方法爲前提。是調用方法的技巧,不會影響到類的內部設計。
class Dog(object):
def __init__(self, name):
self.name = name
def paly(self):
print("Dog {} Play With Game".format(self.name))
class Fly_Dog(object):
def __init__(self, name):
self.name = name
def play(self):
print("Fly_Dog {} Play With Game".format(self.name))
class Person(object):
def __init__(self, name):
self.name = name
def play(self, dog):
print("Person {} Play With Dog {}".format(self.name, dog.name))
P = Person("Bob")
P.play(Dog("Wangcai"))
P.play(Fly_Dog("xiaotian"))
>>> Person Bob Play With Dog Wangcai
Person Bob Play With Dog xiaotian
類屬性
使用面向對象開發,第一步是設計類。使用類名()
創建對象,創建對象的動作有兩步:
- 在內存中爲對象分配空間。‘
- 調用初始化方法
__init__
爲對象初始化
對象創建後,內存中就有一個對象的實實在在的內存(實例)。上述是創建對象的完整過程。
- 若創建了多個對象,每個對象有自己獨立的內存空間,保存各自不同的屬性。
- 多個對象的方法,在內存中只有一份,需要把對象的引用傳遞到方法內部。
在Python
中類是一個特殊的對象,且一切皆對象。在程序運行時,類對象同樣會被加載到內存。
class AAA
:定義的類屬於類對象。obj1 = AAA()
: 屬於實例對象。
在程序運行時,類對象在內存中只有一份,使用一個類可以創建很多個對象實例。除了封裝實例的屬性和方法外,類對象還可以擁有自己的屬性和方法。
class Tool(object):
count = 0
def __init__(self):
Tool.count += 1
a = Tool()
b = Tool()
print(Tool.count)
print(b.count)
>>> 2
2
類屬性用來記錄與這個類相關的特徵,不會記錄具體對象的特徵。如果通過實例化對象來訪問,其會先在實例化對象的方法中找該屬性,沒有的話就會在類裏面去找。因此訪問類屬性有兩種方式:
- 類名.類屬性
- 對象.類屬性(不推薦)
如果這裏程序最後再設置b.count=10
,那麼此時python
解釋器會給實例化的對象b
創建一個屬性,而不是向上去查找類屬性,因此不會改變Tool.count
的值。也就是說使用對象.類屬性=值
賦值語句,只會給對象添加一個屬性,而不會影響到類屬性的值。
類方法
從上文類屬性的文章中我們可以知道,使用賦值語句在class
關鍵字下方可以定義類屬性。
- 類方法就是針對類對象定義的方法。在類方法內部可以直接訪問類屬性,或者調用其他的類方法。
語法如下:
@classmethod
def 類方法名(cls):
pass
類方法需要用修飾器@classmethod
來標識,告訴解釋器這是一個類方法。
第一個參數是cls
。由哪一個類調用的方法,方法內的cls
就是哪一個類的引用,這個參數和實例方法的第一個參數是self
類似,使用其他名稱也可以,不過習慣使用cls
。
通過類名.
調用類方法。調用方法時,不需要傳遞cls
參數,在方法內部可以通過cls.
訪問類的屬性,也可以通過cls.
調用其他類方法。
class Tool(object):
count = 0
@classmethod
def num_of_count(cls):
print("The num of tool is {}".format(cls.count))
def __init__(self):
Tool.count += 1
a = Tool()
a.num_of_count()
>>> The num of tool is 1
靜態方法
開始之前先總結一下類方法和實例方法:1. 如果需要訪問實例屬性,那將這個方法封裝成實例方法。2. 如果某一個方法不需要訪問實例屬性,但是需要訪問類屬性,那麼這個時候可以考慮將這個方法封裝成類方法。
如果某一個方法既不需要訪問其類屬性,也不需要訪問其實例屬性(裏面沒有用到self.
這種操作訪問東西,比如單純的一個print
),那麼可以將這個方法封裝成靜態方法。
語法如下:
@staticmethod
def 靜態方法(): # 不需要指定第一個參數,因爲它什麼都不需要訪問
pass
如:
class Dog(object):
@staticmethod
def run():
print("Run ...")