[note] Python OOP
常用方法
Global Methods
Build-in Functions @ Python Doc
type(obj) # 取得建立該 instance 的 Class
isinstance(obj, Class)
# 如果屬性是會動態改變的
getattr(obj, name, [default_value]) # 拿不到該屬性時不會噴 error
setattr(obj, name, value)
Class
基本使用
- 使用
__init__
初始化物件 - 定義類別屬性(class attribute):直接定義在 class 中
- 定義類別方法(class method):使用
@classmethod
,第一個參數會是 class 本身,常被取名為cls
- 定義實體變數(instance variable):在
__init__
中建立 - 定義實體方法(instance method):直接定義在 class 中,第一個參數會是物件實體(instance)本身,常被取名為
self
- 定義靜態方法(static method):使用
@staticmethod
,預設第一個參數不會帶入任何東西,單純就是個函式
class Dog:
# 建立類別屬性(Class attribute)
species = "Canis lupus familiaris"
# 物件初始化
def __init__(self, name, breed, age):
# 建 立實體變數(instance variable)
self.name = name
self.breed = breed
self.age = age
# 建立類別方法(Class Method)
@classmethod
def all(cls):
return f"All dogs are {cls.species}"
# 建立實體方法(Instance Method)
def bark(self):
return f"{self.name} is barking!"
# 建立靜態方法(Static Method)
@staticmethod
def info():
return "Dogs are loyal animals."
# 建立物件
buddy = Dog("Buddy", "Golden Retriever", 5)
type(Dog) # <class 'type'>
type(buddy) # <class '__main__.Dog'>
type(Dog.bark) # function
type(Dog.all) # method
type(buddy.bark) # method
isinstance(buddy, Dog) # True
# 存取 Class Attribute 和呼叫 Class Method
Dog.species # Dog.species
Dog.all() # 'All dogs are Canis lupus familiaris'
# 存取 Instance variables 和呼叫 Instance Method
buddy.name # 'Buddy'
buddy.bark() # 'Buddy is barking!'
# 呼叫 Static Method
Dog.info() # 'Dogs are loyal animals.'
buddy.info() # 'Dogs are loyal animals.'
在 Python 中,查找 object attribute 的邏輯如下,如果該 object 本身就有該屬性,則會直接拿該屬性的值,例如 buddy.name
;如果該 object 本身沒有該屬性,則會看看它的 class 中有沒有該屬性,例如 buddy.species
會拿到的是 class attribute 的值(即,Dog.species
);如果 instance attribute 和 class attribute 都找不到,而且也不是用 getattr
有預設值的話,則會噴 AttributeError
的錯誤。
這樣的邏輯也同樣使用在類別中的函式或方法,也就是說,當物件實體呼叫了一個方法,如果這個實體本身就含有該實體方法(可以用 vars
或 __dict__
查看),則會直接使用這個實體方法;如果該實體中沒有這個方法,則會到類別中去找有沒有同樣名稱的方法,有的話,就會去呼叫類別中的這個方法;如果還是沒有,會在往上找這個類別是不是繼承其他類別而來,是的話,其上層有沒有這個方法。
如果好奇 Python 會怎麼樣一層一層往上找,可以使用 mro()
這個方法(Method Resolution Order),例如,Dog.__mro__
會的得到 [__main__.Dog, object]
。
使用 __slots__
明確定義該物件的屬性(instance variable)
預設使用者可以自由在物件中添加新的屬性,如果希望這個物件的 instance variables 是固定而不能被任意添加的,則可以用 __slots__
:
- 沒辨法自由在該 instance 添加新的 instance variable,例如,使用
john.height = 180
會拋出AttributeError
- 沒辦法使用
vars
或__dict__
檢視整個物件的實體變數,例如,使用vars(john)
會拋出TypeError
,因為__dict__
屬性已經不能存在
class Person:
__slots__ = ["name", "age"]
def __init__(self, name, age):
self.name = name
self.age = age
john = Person("John", 78)
john.height = 180 # AttributeError: 'Person' object has no attribute 'height'
vars(john) # TypeError: vars() argument must have __dict__ attribute
Getter 和 Setter
可以使用 @property
和 @<property>.setter
來設定 instance variable 的 getter 和 setter,例如:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 使用 @property 定義 age 的 getter
@property
def age(self):
return self.age
# 使用 @xxx.setter 定義 age 的 setter
@age.setter
def age(self, value):
if value < 0:
raise ValueError("Age cannot be negative")
self.age = value
其他
在 Python 中 class
也被稱作 type
class Person:
def __init__(self, name, age, height):
self.name = name
self.age = age
self.__height = height
john = Person("John", 78, 182)
type(john) # __main__.Person
type(Person) # type
- class 的
type
也是type
- type 的
type
也是type
- 所以:type(Person) == type(type) == type`
定義 __ 開頭的 instance variable
如果使用 __
作為 instance variable 的開頭,該 instance variable 的名稱會被加上 class name 當作 prefix。從下面的例子可以看到,原本 __height
的變數,其 instance variable 的名稱變成 _Person__height
:
class Person:
def __init__(self, name, age, height):
self.name = name
self.age = age
self.__height = height
john = Person("John", 78, 182)
vars(john) # {'name': 'John', '_age': 78, '_Person__height': 182}
Instance
取值
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
john = Person("John", 24)
# 使用 "."
john.age # 24
john.foo # AttributeError: 'Person' object has no attribute 'foo'
# 使用 getattr
# getattr(obj, name, [default_value]) # 拿不到該屬性時不會噴 error
getattr(john, "age") # 24
getattr(john, "foo") # AttributeError: 'Person' object has no attribute 'foo'
getattr(john, "foo", "default_value") # "default_value"