# python self staticmethod classmethod dataclass 理解
###### tags: `Python`, `self`, `staticmethod`, `classmethod`, `dataclass`, `工廠模式`, `工廠`
* 關於 `self`
* Python Class 裡的 `self` 會回傳物件本身 (根據物件位址並自動 derefference 為物件)。
* `self` 有點類似 linux kernel 的 macro 中的 [`container_of`](https://hackmd.io/@sysprog/linux-macro-containerof),或 linux kernel 的 macro 中 [`list_entry`](https://github.com/torvalds/linux/blob/master/include/linux/list.h)
```c
// list_entry 就是 container_of,
// 放在 list.h 中,作為 list API 為了命名統一而重新命名
// 用來獲取「封裝 `struct list_head` 的
// container (也是一個 `struct`) 的『address』」
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
```
所以用 linux kernel 的風格來理解的話,==`self` 就是 **The object of object_entry**==。<br>由以下程式碼印出 `self` 訊息,可見 self 就是存於位址 (0x7ffbf95e94c0) 的 calss A 的物件。我所說的 object_entry 就是那段記憶體位址 (0x7ffbf95e94c0)。
```python
class A():
def __init__(self):
print(self)
print(type(self))
# use class `A` to implement `objA` which is a object of calss `A`
objA = A()
# stdout: <__main__.A object at 0x7ffbf95e94c0>
# stdout: <class '__main__.A'>
```
* 一般情況,class 中所有 method 的第一個參數都會被自動傳入 self
* 而 method 的第一個參數 `self` 只是官方慣用命名,其實可以改成任何變數名稱,例如下方範例中,第 5 行命名為 `this`、第 8 行命名為 `object_entry`。<br>有趣的是,`__init__()` 中定義 class 成員是為 `self.value`,但實際上是儲存成 `object_A.value = 0`,因此第 6 行 `set_value()` 可以用 `this.value` 正確存取 `object_A.value`。
* method 會被傳入 the object of object_entry,也就意味只有當 class 實作成 object 之後,才能用 object 呼叫這個 method。因此例外需求就產生了,被加上 `@staticmethod` 的 method 不用建立物件就能透過類別直接呼叫。而加上 `@classmethod` 「**表面上**不用建立物件」可以透過類別直接呼叫。
* 關於 [`staticmethod`](https://docs.python.org/3/library/functions.html#staticmethod)
* 有點類似把 class method 當作一個 package api function
* 由於 staticmethod 沒有 self 參數,因此無法更動或讀取物件內的任何 member 或 method
* 感覺不太實用? 畢竟 Python 實務上通常會把 api 包成一個 package file,而非將各種 api 包進 class。
* 關於 [`classmethod`](https://docs.python.org/3/library/functions.html#classmethod)
* ==第一個參數會自動帶入 **The object of class_entry**==,官方預設命名為 `cls`
* 實務上,classmethod 要用 cls 生成一個物件,並 return 該物件。一般的 class 是回傳一個初始化物件,而 classmethod 生成物件後,可對該物件作額外操作或配置,然後再回傳。
* 可用於「[工廠模式 Factory Pattern](https://ji3g4zo6qi6.medium.com/python-tips-5d36df9f6ad5)」
```python=
class A():
def __init__(self):
self.value = 0
def set_value(this, value):
this.value = this.encryption(value)
def get_value(object_entry):
'''以 linux kernel 風格來說,
self 其實就是 object of class_entry
'''
return object_entry.value
@staticmethod
def encryption(value):
'''就是一個普通的 function
第一個參數並不會被自動帶入 object of object address,
也不會被帶入 object of class address'''
return value+value
@classmethod
def customize_class(cls, setvalue):
'''以 linux kernel 風格來說,
cls 其實就是 object of class_entry
'''
customize_object = cls()
customize_object.set_value(setvalue)
return customize_object
```
```python
object_A = A()
print(object_A.get_value()) # stdout: 0
object_A.set_value(50) # object_A.value = 50 + 50
print(object_A.get_value()) # stdout: 100
object_A_prime = object_A.customize_class(500)
# object_A_prime.value = 500 + 500
print(object_A_prime.get_value())
# stdout: 1000
```
* 關於 [`dataclass`](https://docs.python.org/3/library/dataclasses.html)
* 中文網誌: https://www.maxlist.xyz/2022/04/30/python-dataclasses/
* 帶有 `@dataclass` 標記的 class 會自動獲得 python 常用的 special methods 如:`__init__()` and `__repr__()`,可以使 pyhton code 更精簡
* `@` 在此稱為 `python Decorator`