在面向對象的術語中,類當X
擴展類Y
時,則Y
稱爲該等超級/父/基類,X
稱爲子類/子/派生類。這裏需要注意的一點是,只有數據字段和非專用的方法才能被子類訪問。私有數據字段和方法只能在類中訪問。
1,創建³³首先了一個名爲Date
的類,並將該對象作爲參數傳遞,object
是由Python中提供的內置類。創建³³之後了另一個名爲time
的類,並將Date
類稱爲參數。通過這個調用,可以訪問Date
類中的所有數據和屬性。正因爲如此,的創建³³ Time
類對象tm
中父電子雜誌類中get_date
方法。
2,在Python中的中,類的構造函數用於創建對象(實例),併爲屬性賦值。
子類的構造函數總是調用父類的構造函數來初始化父類中的屬性的值,然後它開始爲其屬性賦值。
3,在Python中的中,類的構造函數用於創建對象(實例),併爲屬性賦值。
子類的構造函數總是調用父類的構造函數來初始化父類中的屬性的值,然後它開始爲其屬性賦值。
class Animal(object):
def __init__(self, name):
self.name = name
def eat(self, food):
print('%s is eating %s , '%(self.name, food))
class Dog(Animal):
def fetch(self, thing):
print('%s goes after the %s !'%(self.name, thing))
class Cat(Animal):
def swatstring(self):
print('%s shreds the string! ' % (self.name))
d = Dog('Ranger')
c = Cat("Meow")
d.fetch("ball");
c.swatstring()
d.eat("Dog food")
c.eat("Cat food")
## 調用一個沒有的定義的方法
#d.swatstring()
在上面的例子中,我們看到如何父類中的屬性或方法,以便所有的子類或子類都會從父類繼承那些屬性。
如果一個子類嚐嚐從另一個子類繼承方法或數據,那麼它將通過一個錯誤,就像看到當狗類嘗試調用cat
類有swatstring()
方法時,它會拋出一個錯誤(AttributeError
)。
我們用之前的繼承示例理解多態的概念,並在兩個子類中添加一個名爲show_affection
的常用方法 -
從這個例子可以看到,它指的是一種設計,其中不同類型的對象可以以相同的方式處理,或者更具體地說,兩個或更多的類使用相同的名稱或通用接口,因爲同樣的方法(下面的示例中的show_affection
)用任何一種類型的對象調用
4,重載
在Python中,當子類包含一個覆蓋超類方法的方法時,也可以通過調用超類方法 -
Super(Subclass, self).method
而不是self.method
。
繼承構造函數
從前面的繼承示例中看到,__init__
位於父類中,因爲子類 - Dog
或Cat
沒有__init__
方法.Python使用繼承屬性查找來查找動物類中的__init__
。
當我們創建子類時,首先它會查找dog
類中的__init__
方法,如果它找不到它,則查找父類Animal
,並在那裏找到並在那裏調用它。
因此,當類設計變得複雜時,可能希望初始化一個實例,
首先通過父類構造函數然後通過子類構造函數處理它。
import random
class Animal(object):
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name):
super(Dog, self).__init__(name)
self.breed = random.choice(['Doberman', 'German shepherd', 'Beagle'])
def fetch(self, thing):
print('%s goes after the %s !'%(self.name, thing))
d = Dog('黑皮')
print(d.name)
print(d.breed)
結論
-
__init__
與任何其他方法一樣; 它可以被繼承 -
如果一個類沒有
__init__
構造函數,巨蟒將檢查其父類查找。 -
只要找到一個有
__init__
構造函數,巨蟒就會調用它並停止查找 -
可以使用
super()
函數來調用父類中的方法。 -
可能想要在父類以及子類進行初始化。
總結如下 -
-
任何類都可以從多個類繼承
-
搜索繼承類時,巨蟒通常使用“深度優先”順序。
-
但是,當兩個類從同一個類繼承時,巨蟒將從該MRO中消除該類的第一次出現
裝飾器,靜態和類方法
函數(或方法)由def
語句創建³³³³。
雖然方法的工作方式與函數完全相同,除了方法第一個參數是實例對象的一點。
我們可以根據行爲方式來分類方法
- 簡單的方法 - 在類的外部定義。該函數可以通過提供實例參數來訪問類屬性:
def outside_func(():
蟒蛇
- 實例方法 -
def func(self,)
蟒蛇
- 類方法 - 如果需要使用類屬性
@classmethod def cfunc(cls,)
蟒蛇
- 靜態方法 - 沒有關於該類的任何信息
@staticmethod def sfoo()
蟒蛇
到目前爲止,我們已經看到了實例方法,下面來了解其他兩種方法。
類方法
@classmethod
裝飾器是一個內置的函數裝飾器,它通過調用它的類或作爲第一個參數調用的實例的類。評估結果會影響函數定義。
語法
class C(object):
@classmethod
def fun(cls, arg1, arg2, ...):
....
fun: function that needs to be converted into a class method
returns: a class method for function
蟒蛇
他們有權訪問此cls
參數,它不能修改對象實例狀態。
- 它受到類的約束,而不是類的對象。
- 類方法仍然可以修改適用於類的所有實例的類狀態。
2.靜態方法
靜態方法既不接受自己也不接受cls(class)
參數,但可以自由接受任意數量的其他參數。
語法
class C(object):
@staticmethod
def fun(arg1, arg2, ...):
...
returns: a static method for function funself.
蟒蛇
- 靜態方法既不能修改對象狀態,也不能修改類的狀態。
- 受限於可以訪問的數據。
什麼時候使用
- 通常使用類方法來創建工廠方法。工廠方法返回不同用例的類對象(類似於構造函數)。
- 通常使用靜態方法來創建實用函數。
如圖1所示,還有一種把數據和功能結合起來,用稱爲對象的東西包裹起來組織程序的方法。
類和對象是面向對象編程的兩個主要方面。類創建一個新類型,而對象這個類的實例。這類似於你有一個int
類型的變量,這存儲整數的變量是int
類的實例(對象)。
類的方法與普通的函數只有一個特別的區別-它們必須有一個額外的第一個參數名稱,但是調用在這個方法的時候你不。爲這個參數賦值,Python中會提供這個值這個特別的變量指對象本身,按照慣例它的名稱是self
。
可以注意到存儲對象的計算機內存地址也打印了出來。這個地址在你的計算機上會是另外一個值,因爲Python中可以在任何空位存儲對象。
在Python中的類中有很多方法的名字有特殊的重要意義。我們現在學習將__init__
方法的意義。
__init__
方法在類的一個對象被建立時,馬上運行。這個方法可以用來對你的對象做一些你希望的初始化。注意,這個名稱的開始和結尾都是雙下劃線。
這裏,把我們__init__
方法定義爲取一個參數name
(以及普通的參數self
)。這個在__init__
裏,我們只是創建一個新的域,也稱爲name
。注意它們是兩個不同的變量,儘管它們有相同的名字。點號使我們能夠區分它們。
最重要的是,沒有我們專門調用__init__
方法,只是在創建一個類的新實例的時候,把參數包括在圓括號內跟在類名後面,傳遞從而給__init__
方法。這是這種方法的重要之處。
現在,能夠我們在我們的方法中使用self.name
域。在這sayHi
方法中得到了驗證。
當子類和父類都存在相同的run()
方法時,我們說,子類的run()
覆蓋了父類的run()
,在代碼運行的時候,總是會調用子類的run()
。這樣,我們就獲得了繼承的另一個好處:多態。
要理解什麼是多態,我們首先要對數據類型再作一點說明。當我們定義一個class的時候,我們實際上就定義了一種數據類型。我們定義的數據類型和Python自帶的數據類型,比如str、list、dict沒什麼兩樣
a = list() # a是list類型
b = Animal() # b是Animal類型
c = Dog() # c是Dog類型
Dog
可以看成Animal
,但Animal
不可以看成Dog
。
要理解多態的好處,我們還需要再編寫一個函數,這個函數接受一個Animal
類型的變量:
def run_twice(animal):
animal.run()
animal.run()
多態的好處就是,當我們需要傳入Dog
、Cat
、Tortoise
……時,我們只需要接收Animal
類型就可以了,因爲Dog
、Cat
、Tortoise
……都是Animal
類型,然後,按照Animal
類型進行操作即可。由於Animal
類型有run()
方法,因此,傳入的任意類型,只要是Animal
類或者子類,就會自動調用實際類型的run()
方法,這就是多態的意思:
對於Python這樣的動態語言來說,則不一定需要傳入Animal
類型。我們只需要保證傳入的對象有一個run()
方法就可以了:
class Timer(object):
def run(self):
print('Start...')
繼承可以把父類的所有功能都直接拿過來,這樣就不必重零做起,子類只需要新增自己特有的方法,也可以把父類不適合的方法覆蓋重寫。
動態語言的鴨子類型特點決定了繼承不像靜態語言那樣是必須的。
類的方法與普通的函數只有一個特別的區別——它們必須有一個額外的 第一個參數名稱
按照慣例它的名稱是 self
#!/usr/bin/python
# -*- coding: UTF-8 -*-
class Employee:
'''所有員工的基類'''
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def displayCount(self):
print "Total Employee %d" % Employee.empCount
def displayEmployee(self):
print "Name : ", self.name, ", Salary: ", self.salary
其中
-
empCount 變量是一個類變量,它的值將在這個類的所有實例之間共享
可以在內部類或外部類使用 Employee.empCount 訪問
-
init() 是一個特殊的方法,被稱爲類的構造函數或初始化方法,當創建了這個類的實例時就會調用該方法
-
self 代表類的實例,self 在定義類的方法時是必須有的,雖然在調用時不必傳入相應的參數
self 代表類的實例,而非類
類的方法與普通的函數只有一個特別的區別——它們必須有一個額外的 第一個參數名稱
按照慣例它的名稱是 self
我們已經創建好了一個類 Employee
,如果要創建這個類的實例,只需要使用 類名+() 即可
就像調用函數一樣
類的實例會通過 __init__()
方法接受參數
垃圾回收機制不僅針對引用計數爲 0 的對象,同樣也可以處理循環引用的情況
循環引用指的是,兩個對象相互引用,但是沒有其他變量引用他們
這種情況下,僅使用引用計數是不夠的
Python 的垃圾收集器實際上是一個引用計數器和一個循環垃圾收集器
作爲引用計數的補充, 垃圾收集器也會留心被分配的總量很大 ( 及未通過引用計數銷燬的那些 ) 的對象
在這種情況下, 解釋器會暫停下來, 試圖清理所有未引用的循環
# -*- coding: UTF-8 -*-
class Point:
def __init__( self, x=0, y=0):
self.x = x
self.y = y
def __del__(self):
class_name = self.__class__.__name__
print class_name, "銷燬"