类方法有点甜
Python 中 class
可以自定义类方法, 大多数时候用着很顺手, 到底 @staticmethod, @classmethod
这两个语法糖 “甜” 在哪儿呢 ?
如果你经常把 __init__
函数写的很臃肿, 或者在类外部定义了很多函数, 那你一定会喜欢这两粒语法糖.
@classmethod
@classmethod
对于多态很关键, 允许用户更加自由地创建实例。
例如
Python 中的变量都无类型, 显然可以用 isinstance
进行判断然后执行不同的构造方法。
当然也有更简明的方法, 就是针对不同类型输入显式调用不同的构造函数。
比如我创建了一个用来表示日期的类1
2
3
4
5
6class Date(self):
def __init__(self, year: int, month: int):
self.year = year
self.month = month
date = Date(2022, 5)
但是有时候输入的数据类型并非如我所愿, 如 Date('2020-5')
。 如果在 __init__
中进行判断会有些麻烦, 而 Python 又不支持定义多个构造函数, 怎么办。
可以用 @classmethod
给出一个实现:1
2
3
4
5
6
7
8
9
10
11class Date(self):
def __init__(self, year: int, month: int):
self.year = year
self.month = month
def from_string(cls, string):
year, month = map(int, string.split('-'))
return cls(year, month)
date = Date.from_string('2020-5')
进一步的, 如果此时用户的输入是内置类型 datetime.date
那么我们只需要再添加一个 classmethod
, 非常简洁。
1 | from datetime import datetime |
但是听说 Guido 曾经因为觉得 @classmethod
没什么人用, 打算把它删掉, 幸好没有。
@staticmethod
@staticmethod
其实与定义在类外部的函数没有本质区别, 主要是为了封装的方便和 namespace 的整洁。例如在深度学习项目中, 我们对不同模型需要定义不同损失函数:
1 | def dice_loss_for_maskformer(inputs, targets): |
这样对每个 dice_loss
分别命名不够简洁, 但是用 @staticmethod
可以在不占用外部 namespace, 统一宣称函数名。如:1
2
3
4
5
6
7
8
9
10
11
12
13class SetCriterionForMaskformer(nn.Module):
def dice_loss(inputs, targets):
pass
def forward(self, inputs, targets):
return dice_loss_for_maskformer(inputs, targets)
class SetCriterionForUnet(nn.Module):
def dice_loss(inputs, targets):
pass
def forward(self, inputs, targets):
return dice_loss_for_unet(inputs, targets)