CrossEntropyLoss
的类的语法。我关心的部分是这样的:
class CrossEntropyLoss(_WeightedLoss):
__constants__ = ['ignore_index', 'reduction', 'label_smoothing']
ignore_index: int
label_smoothing: float
def __init__(self, weight: Optional[Tensor] = None, size_average=None, ignore_index: int = -100,
reduce=None, reduction: str = 'mean', label_smoothing: float = 0.0) -> None:
super().__init__(weight, size_average, reduce, reduction)
self.ignore_index = ignore_index
self.label_smoothing = label_smoothing
[...]
我无法弄清楚第三行
ignore_index: int
和第四行label_smoothing: float
的含义是什么。
它们看起来像
ignore_index
和 label_smoothing
的类型提示,但这些信息在 __init__
函数中似乎是多余的。
除此重复之外,以下类在两个地方使用不同的类型,并且不会在
mypy
中给出任何错误。
#!/usr/bin/env python3
class A:
num: float
def __init__(self, num: int = 5):
self.num = num
a = A()
print("type(a.num) = " + str(type(a.num)))
# both lines give
## AttributeError: type object 'A' has no attribute 'num'
print("type(A.num) = " + str(type(A.num)))
print("A.num = " + str(A.num))
使用 Python 3.12 执行它会给出:
$ ./static_var_test.py
type(a.num) = <class 'int'> <--------------------- NOTE: not float
Traceback (most recent call last):
File "./static_var_test.py", line 16, in <module>
print("type(A.num) = " + str(type(A.num)))
^^^^^
AttributeError: type object 'A' has no attribute 'num'
mypy
的输出:
$ mypy --version
mypy 1.8.0 (compiled: yes)
$ mypy static_var_test.py
Success: no issues found in 1 source file
类顶层的类型提示有何用处?
不同情况的一些前奏:默认与未设置以及实例与类。
虽然没有必要定义类型提示注释,但可以区分这些用例,请参阅PEP 526。
首先,您可以使用值显式声明变量(不一定需要类型提示。) 但是,存在一个问题,您是否希望整个 class 的属性在所有实例之间共享 (
ClassName.shared
),还是希望为每个 instance (Apple("Big").price
) 提供默认值。
官方的例子是一艘船,同级别的每艘船应该有相同的属性。 然而,每个实例都可以有自己的队长,但您想要声明一个默认值并且可能更改/访问它,从函数访问默认参数不是很干净。 由于实例默认值和类默认值是共享的,因此有
ClassVar
类型提示来区分这些情况。
class BasicStarship:
captain: str = 'AI' # instance variable with default
damage: int # instance variable without default
stats: ClassVar[Dict[str, int]] = {} # class variable
def __init__(self, damage: int, captain: str = None):
self.damage = damage
if captain:
self.captain = captain # Else keep the default
类顶层的类型提示有何用处?
一个论点是您可以更轻松地跟踪要实现的默认实例值。 如果您有一些带有自定义 init 函数的继承层次结构,您可能无法 100% 确定所有类是否都实现所有默认值。 这使得它更类似于 C 或 Java,您也可以在其中了解此概述; TL;DR 和我认为更干净的编程。
它们与 init 的参数并不多余。 后者可能更全面,例如如果允许多种类型,并且在最终值之前进行一些预处理
class Coin:
values : List[int]
def __init__(self, values : Union[List[int], int]):
# fix single values entries
if not isisntance(Iterable, values):
self.values = [values] # correct type from 2 allowed.
此外,注释与数据类
完美结合