Typescript接口的Python等价物

pxy2qtax  于 2023-05-01  发布在  TypeScript
关注(0)|答案(8)|浏览(148)

最近我一直在使用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' }
    ]
}

相当简洁,并提供了所有的奢侈品,如类型检查和代码完成,而编码的人。
在Python中如何做到这一点?
我一直在看Mypy和ABC,但还没有成功地找到Python的方式来做类似的事情,如上所述(我的尝试导致了太多的样板,我的口味)。

aiazj4mn

aiazj4mn1#

对于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也可以识别这些存根文件,如果您的原始模块不是静态类型的,它们将使用存根文件进行类型提示和代码完成。

yhxst69z

yhxst69z2#

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

TypeScript接口示例

例如TypeScript接口:

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

将描述JavaScript对象,如:

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

Python TypedDict示例

等价的Python 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序列化,并且符合类似的TypeScript接口类型。
注意:如果您使用的是Python 2或Python 3的旧版本,则可能需要使用TypedDict的旧版本的基于函数的语法:

from mypy_extensions import TypedDict

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

备选方案

Python中还有其他方法可以用命名属性来表示结构。

Named tuple价格便宜,而且有只读键。但是,它们不能自动序列化到/从JSON序列化。

from typing import NamedTuple

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

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

Data classes,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,
)

Simple namespaces,在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类型检查器识别。

import attrs

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

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

bvpmtnay3#

Python 3.6添加了一个新的namedtuple实现,它使用类型提示,这删除了其他答案所需的一些样板。

from typing import NamedTuple, Optional, List

class Address(NamedTuple):
    street: str
    housenumber: int
    housenumberPostfix: Optional[str] = None

class Person(NamedTuple):
    name: str
    adresses: List[Address]

person = Person(
    name='Joe',
    adresses=[
        Address(street='Sesame', housenumber=1),
        Address(street='Baker', housenumber=221, housenumberPostfix='b'),
    ],
)

编辑:NamedTuple是不可变的,所以要注意,如果你想修改对象的字段,你不能使用这个解决方案。更改listsdicts的内容仍然可以。

vcudknz3

vcudknz34#

我发现了一个简单的解决方案(不需要Python 3。7)使用SimpleNamespace:

from types import SimpleNamespace as NS
from typing import Optional, List

class Address(NS):
    street: str
    housenumber: int
    housenumber_postfix: Optional[str]=None

class Person(NS):
    name: str
    addresses: List[Address]

person = Person(
    name='Joe',
    addresses=[
        Address(street='Sesame', housenumber=1),
        Address(street='Baker', housenumber=221, housenumber_postfix='b')
    ])
  • 这在Python 3中有效。3及以上
  • 字段是可变的(与NamedTuple解决方案不同)
  • 代码完成似乎在PyCharm中完美地工作,但在VSCode中不是100%(为此提出了issue
  • 在mypy中的类型检查工作,但PyCharm不抱怨,如果我。g do person.name = 1

如果有人能指出为什么Python 3.7的dataclass装饰器会更好,我很乐意听到。

ha5z0ras

ha5z0ras5#

也许这在mypy中会很好地工作

from typing import List
from mypy_extensions import TypedDict

EntityAndMeta = TypedDict("EntityAndMeta", {"name": str, "count": int})

my_list: List[EntityAndMeta] = [
  {"name": "Amy", "count": 17},
  {"name": "Bob", "count": 42},
]

从mypy文档或source code阅读更多关于 TypedDict 的信息
我很确定你可以设置为nest these things,如果你愿意的话,可以将其中一些设置为Optional
我从https://stackoverflow.com/a/21014863/5017391得到这个想法

bvuwiixz

bvuwiixz6#

尝试https://github.com/cs-cordero/py-ts-interfaces
看上去很不错。报价:
在后端使用Python而前端使用TypeScript的Web应用程序中,通常情况下,客户端将调用后端以请求具有某些特定预定义“形状”的数据。在客户端,通常会为这些数据定义一个接口,如果Python后端作者使用类型检查(如mypy),项目作者可能也会输入JSON响应值。
这会导致代码重复。如果形状在后端发生变化,相关的界面也必须在前端反映其变化。在最好的情况下,这是恼人的维护。在最坏的情况下,随着时间的推移,接口可能会出现分歧并导致错误。
这个库的目标是拥有一个单一的真相来源,用来描述后端和前端之间的有效载荷的形状。

daupos2t

daupos2t7#

在Python 35、可以使用注解来指定参数的类型和返回类型。最近的大多数IDE,如PyCharm,都可以解释这些注解,并为您提供良好的代码完成功能。您也可以使用注解来指定函数的签名或变量的类型。
下面是一个例子:

from typing import List, Optional

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

class Person(object):
    def __init__(self, name: str, addresses: List[Address]):
        self.name = name
        self.addresses = addresses

person = Person(
    name='Joe',
    addresses=[
        Address(street='Sesame', housenumber=1),
        Address(street='Baker', housenumber=221, housenumber_postfix='b')
    ])

请注意,Python不是一种强类型语言。因此,注解只是开发人员的指南。如果你真的想检查你的代码,你需要一个外部工具(目前,最好的是mypy)。它可以像任何其他代码检查器一样在代码质量控制期间使用。

xu3bshqb

xu3bshqb8#

你们觉得pydantic怎么样?Python 3.10

from pydantic import BaseModel

class Address(BaseModel):
    street: str
    housenumber: int
    housenumberPostfix: str | None = None

class Person(BaseModel):
    name: str
    adresses: list[Address]

person: Person = Person(
    name="Joe",
    adresses=[
        Address(street="Sesame", housenumber=1),
        Address(street="Baker", housenumber=221, housenumberPostfix="b"),
    ],
)

当我们输入错误的类型。
Editor complain wrong type
它有建议。
Editor show suggestion

相关问题