在寫程式的風格裡有分為物件導向、函數式導向、程序性導向三種風格.物件在物件導向程式設計是很重要的一環,物件有自身的狀態以及方法.而 classes 就是定義這些狀態(屬性)跟方法,而物件就是 classes 實體化後的 instance.
用 type 可以看這個'Python'
這個 string 物件是哪個 class 實體化的.
>>> type('Python')
<class 'str'>
當要定義一個 class 時要思考,要定義 class 的名稱、有哪些屬性以及提供哪些方法.
使用 class 關鍵字定義一個 class.和定義 class 的方法,在 class 的方法裡第一個參數要給一個 self,表示該 instance.
>>> class Animal:
... def move(self,action):
... print('{}'.format(action))
...
定義好後,透過建構子把 Animal 實體化,變成一個 instance 也就是物件,然後透過 a1 這變數指到該物件.
>>> a1 = Animal()
接著就可以使用該物件的方法.
>>> a1.move('running')
running
>>> a1.move('flying')
flying
上面的寫法其實相當於這樣寫,a1 也就是 self 該 instance.
>>> Animal.move(a1,'jump')
jump
其實不一定要叫 self,改成其它的名稱也是可以,但為了可讀性還是統一使用 self 比較好.
>>> class Animal:
... def move(test_self,action):
... print('{}'.format(action))
...
>>> a1 = Animal()
>>> a1.move('flying')
flying
接著幫 Animal 定義屬性,定義__init__
這 function 讓在實體化時多給個參數 name,再把這個參數帶到 self 的 name 裡.__init__
稱為建構子,可以設定一些產生物件時需要先初始化的一些過程.
>>> class Animal:
... def __init__(self,name):
... self.name = name
... def move(self,action):
... print('{} is {}'.format(self.name , action))
...
>>> a1 = Animal('Dog')
>>> a1.move('running')
Dog is running
>>> a1.name
'Dog'
使用 dir 看 Animal 個 class 並沒有 name 這屬性.
>>> dir(Animal)
['__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__', 'move']
但看 a1 這物件可以多看到一個 name 屬性.
>>> dir(a1)
['__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__', 'move', 'name']
所以 name 屬於實體屬性,只有實體才能使用到,另外還有一種是類別屬性.類別屬性跟方法一樣定義在 class 的下一層.例如下面的 age 屬性.
然後在使用類別屬性時要加上類別名稱
.
>>> class Animal:
... age = 0
... def __init__(self,name):
... self.name = name
... def add_age(self,new_age):
... Animal.age = new_age
... def pring_age(self):
... print(Animal.age)
...
類別屬性會影響到所有用該類別創建的 instance 也就是物件,只要類別屬性的值改變,每個 instance 都會更新.
>>> a1 = Animal('Cat')
>>> a1.add_age(5)
>>> a1.pring_age()
5
>>> a2 = Animal('Dog')
>>> a2.pring_age()
5
>>> Animal.age
5
>>> Animal.age = 10
>>> a1.pring_age()
10
>>> a2.pring_age()
10
存取類別屬性也可以用cls
.使用的方式跟self
有點像,也是第一個參數,只不過 cls 指的就不是實體了,而是該類別.
>>> class Animal:
... age = 0
... def __init__(self,name):
... self.name = name
... def add_age(cls,new_age):
... cls.age = new_age
... def pring_age(cls):
... print(cls.age)
...
>>> a1 = Animal('Cat')
>>> Animal.age = 5
>>> a1.pring_age()
5
>>> a2 = Animal('Dog')
>>> a2.pring_age()
5
python 再找屬性時會先從物件屬性開始找,找不到再找類別屬性,所以可以透過物件找到類別屬性,如果都找不到就會出現 AttributeError.
>>> Animal.age
10
>>> a2.age
10
>>> a2.aaa
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Animal' object has no attribute 'aaa'
屬性實際上的值其實是存在__dict__
這 dictionary 裡.可以看到一開始類別屬性 age 並沒有存在裡面,當改變了 age 的值,就會存到__dict__
.
>>> class Animal:
... age = 0
... def __init__(self,name):
... self.name = name
... def add_age(cls,new_age):
... cls.age = new_age
... def pring_age(cls):
... print(cls.age)
...
>>> a1 = Animal('Cat')
>>> type(a1.__dict__)
<class 'dict'>
>>> a1.__dict__
{'name': 'Cat'}
>>> a1.add_age(5)
>>> a1.__dict__
{'name': 'Cat', 'age': 5}
在 python 幾乎都是物件,所以都會有個__class__
屬性,可以看到參考的類別.
>>> ''.__class__
<class 'str'>
>>> Animal.__class__
<class 'type'>
>>> a1.__class__
<class '__main__.Animal'>
>>> type(a1.__class__)
<class 'type'>