没有初始值的变量的类型注释

10

我一直在阅读PEP484和526,但仍然无法弄清楚如何更好地对没有初始值的变量进行类型注释。

比如说,你有一个类,在__init__中想要声明一个变量,但是不提供初始化值,而是在代码的后面进行赋值。通常我会这样做:

from typing import Optional, List

class SomeClass:

    def __init__(self) -> None:
        self.some_value: Optional[int] = None
        self.other_var: Optional[List] = None

    def _some_method(self) -> None:
        self.some_value = 42

这样做是有效的,但我觉得使用 Optional 会让 __init__ 方法变得过于繁忙和难以阅读。相反,我可以这样做:

from typing import List

class SomeClass:

    def __init__(self) -> None:
        self.some_value: int
        self.other_var: List

    def _some_method(self) -> None:
        self.some_value = 42

显然,这会导致变量未初始化,但只要在引用它们之前正确地分配它们,一切都能正常工作,并且如果我在__init__中声明了许多类属性,则这样做可以使代码更易于阅读。
使用第二种方法留下未初始化的变量可能会导致特定问题吗?

类型注解不会影响脚本的行为,它们只是用来告知程序员意图的。怎么可能会有任何问题呢? - Barmar
1
self.some_value: int = None是可以的。就像在有类型的语言中声明但不初始化一样。 - user10325516
2
暂时忘记注释。你想要什么“行为”?你想要将属性初始化为None吗?还是你想要属性被取消设置?选择你想要的“行为”,然后使用适合实现该行为的代码注释。 - user2357112
@Barmar,我应该澄清一下,我特别想知道是否会有任何特定问题出现在变量未初始化的情况下(第二种方法)?而不是从类型注释本身来看。 - NotAName
这取决于函数对变量的操作。但是通常情况下,在需要存储数据之前,将变量保持未初始化是很常见的做法。 - Barmar
@Poolka:从运行的角度来说,它是“可以”的,不会抛出任何错误,但从语义上来说是不正确的,类型检查器会报错。 - user2357112
2个回答

2
有没有在使用第二种方法时留下变量未初始化可能出现的特定问题?
是的。添加类型注释而不设置值仅供类型检查器使用。实际上并不会创建变量。只需看一下从您的第二个示例中抛出的错误即可。
from typing import List


class SomeClass:
    def __init__(self) -> None:
        self.some_value: int
        self.other_var: List

    def _some_method(self) -> None:
        self.some_value = 42


sc = SomeClass()
print(sc.some_value)

Traceback (most recent call last):
  File ".../test.py", line 14, in <module>
    print(sc.some_value)
          ^^^^^^^^^^^^^
AttributeError: 'SomeClass' object has no attribute 'some_value'

请注意,错误是“没有属性”。换句话说,根本没有创建该属性。
从Python 3.10开始,语法变得更加简洁,因此您不再需要从typing导入,也不再需要Optional。以下是我为您的类定义类型的方式:
class SomeClass:
    def __init__(self) -> None:
        self.some_value: int | None = None
        self.other_var: list[int] | None = None

    def _some_method(self) -> None:
        self.some_value = 42

1

如果你不喜欢__init__方法变得过于拥挤,你可以将私有属性和公共属性分开,并且仅对后者进行注释。

class SomeClass:
    def __init__(self) -> None:
        self._some_value = None
    
    @property
    def some_value(self) -> Optional[int]:  # int | None in Python >= 3.10
        return self._some_value
    
    @some_value.setter
    def some_value(self, value):
        self._some_value = value

    def _some_method(self) -> None:
        self.some_value = 42

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接