Python 相当于 Typescript 接口

新手上路,请多包涵

最近我一直在使用 Typescript,它允许表达如下内容:

 interface Address {
    street: string;
    housenumber: number;
    housenumberPostfix?: string;
}

interface Person {
    name: string;
    adresses: Address[]
}

const person: Person = {
    name: 'Joe',
    adresses: [
        { street: 'Sesame', housenumber: 1 },
        { street: 'Baker', housenumber: 221, housenumberPostfix: 'b' }
    ]
}

非常简洁,并在使用 Persons 编码时提供了类型检查和代码完成等所有功能。

这是如何在 Python 中完成的?

我一直在研究 Mypy 和 ABC,但还没有成功地找到 pythonic 方法来做与上述类似的事情(我的尝试导致了太多符合我口味的样板文件)。

原文由 Otto 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

对于 IDE 中的代码完成和类型提示,只需为 PersonAddress 类添加静态类型,你就可以开始了。假设您使用最新的 python3.6 ,这是您示例中的打字稿类的粗略等效项:

 # spam.py
from typing import Optional, Sequence

class Address:
    street: str
    housenumber: int
    housenumber_postfix: Optional[str]

    def __init__(self, street: str, housenumber: int,
                 housenumber_postfix: Optional[str] = None) -> None:
        self.street = street
        self.housenumber = housenumber
        self.housenumber_postfix = housenumber_postfix

class Person:
    name: str
    adresses: Sequence[Address]

    def __init__(self, name: str, adresses: Sequence[str]) -> None:
        self.name = name
        self.adresses = adresses

person = Person('Joe', [
    Address('Sesame', 1),
    Address('Baker', 221, housenumber_postfix='b')
])  # type: Person

我想您提到的样板是在添加类构造函数时出现的。这确实是不可避免的。我希望在未明确声明时在运行时生成默认构造函数,如下所示:

 class Address:
    street: str
    housenumber: int
    housenumber_postfix: Optional[str]

class Person:
    name: str
    adresses: Sequence[Address]

if __name__ == '__main__':
    alice = Person('Alice', [Address('spam', 1, housenumber_postfix='eggs')])
    bob = Person('Bob', ())  # a tuple is also a sequence

但不幸的是你必须手动声明它们。


编辑

正如 Michael0x2a评论 中指出的那样,在 python3.7 中可以避免对默认构造函数的需求,它引入了 @dataclass 装饰器,因此确实可以声明:

 @dataclass
class Address:
    street: str
    housenumber: int
    housenumber_postfix: Optional[str]

@dataclass
class Person:
    name: str
    adresses: Sequence[Address]

并获取几个方法的默认实现,减少样板代码的数量。查看 PEP 557 了解更多详情。


我想您可能会看到可以从您的代码生成的存根文件,作为某种接口文件:

 $ stubgen spam  # stubgen tool is part of mypy package
Created out/spam.pyi

生成的存根文件包含模块的所有非私有类和函数的类型化签名,但没有实现:

 # Stubs for spam (Python 3.6)
#
# NOTE: This dynamically typed stub was automatically generated by stubgen.

from typing import Optional, Sequence

class Address:
    street: str
    housenumber: int
    housenumber_postfix: Optional[str]
    def __init__(self, street: str, housenumber: int, housenumber_postfix: Optional[str]=...) -> None: ...

class Person:
    name: str
    adresses: Sequence[Address]
    def __init__(self, name: str, adresses: Sequence[str]) -> None: ...

person: Person

这些存根文件也被 IDE 识别,如果您的原始模块不是静态类型的,它们将使用存根文件进行类型提示和代码完成。

原文由 hoefling 发布,翻译遵循 CC BY-SA 3.0 许可协议

TypeScript 接口描述了一个 JavaScript 对象。这样的对象类似于具有众所周知的字符串键的 Python 字典,它由 mypy TypedDict 描述。

TypeScript 接口示例

例如 TypeScript 接口:

 interface Address {
    street: string;
    housenumber: number;
}

将描述 JavaScript 对象,例如:

 var someAddress = {
    street: 'SW Gemini Dr.',
    housenumber: 9450,
};

mypy TypedDict 示例

等效的 mypy TypedDict

 from typing import TypedDict

class Address(TypedDict):
    street: str
    housenumber: int

将描述 Python 字典,例如:

 some_address = {
    'street': 'SW Gemini Dr.',
    'housenumber': 9450,
}

# or equivalently:

some_address = dict(
    street='SW Gemini Dr.',
    housenumber=9450,
)

这些字典可以很容易地序列化为 JSON 或从 JSON 序列化,并且将符合类似的 TypeScript 接口类型。

注意:如果您使用的是 Python 2 或旧版本的 Python 3,您可能需要为 TypedDict 使用旧的基于函数的语法:

 from mypy_extensions import TypedDict

Address = TypedDict('Address', {
    'street': str,
    'housenumber': int,
})

备择方案

在 Python 中还有其他方法来表示具有命名属性的结构。

命名元组 很便宜并且有只读键。但是,它们不能自动序列化到 JSON 或从 JSON 自动序列化。

 from typing import NamedTuple

class Address(NamedTuple):
    street: str
    housenumber: int

my_address = Address(
    street='SW Gemini Dr.',
    housenumber=9450,
)

Python 3.7 中可用的 数据类 具有读写键。它们也不能自动序列化为 JSON 或从 JSON 序列化。

 from dataclasses import dataclass

@dataclass
class Address:
    street: str
    housenumber: int

my_address = Address(
    street='SW Gemini Dr.',
    housenumber=9450,
)

Python 3.3 中可用的 简单命名空间 类似于数据类,但不是很为人所知。

 from types import SimpleNamespace

class Address(SimpleNamespace):
    street: str
    housenumber: int

my_address = Address(
    street='SW Gemini Dr.',
    housenumber=9450,
)

attrs 是一个历史悠久的第三方库,类似于数据类,但具有更多功能。 attrs 被 mypy typechecker 识别

 import attrs

@attr.s(auto_attribs=True)
class Address:
    street: str
    housenumber: int

my_address = Address(
    street='SW Gemini Dr.',
    housenumber=9450,
)

原文由 David Foster 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题