相当于 Typescript 接口的 Python
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/48254562/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python equivalent of Typescript interface
提问by Otto
Recently I have been working with Typescript a lot, it allows to express things like:
最近我一直在使用 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' }
]
}
Pretty concise and giving all the luxuries as type checking and code completion while coding with Persons.
非常简洁,并在使用 Persons 编码时提供所有类型的检查和代码完成。
How is this done in Python?
这是如何在 Python 中完成的?
I have been looking at Mypy and ABC but did not yet succeed in finding the pythonic way to do something similar as the above (my attempts resulted in way too much boilerplate to my taste).
我一直在研究 Mypy 和 ABC,但还没有成功找到 Pythonic 方法来做与上述类似的事情(我的尝试导致了太多的样板文件,不合我的口味)。
采纳答案by hoefling
For the code completion and type hinting in IDEs, just add static typing for the Person
and Address
classes and you are already good to go. Assuming you use the latest python3.6
, here's a rough equivalent of the typescript classes from your example:
对于 IDE 中的代码完成和类型提示,只需为Person
和Address
类添加静态类型,您就可以开始了。假设您使用的是 latest 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
I suppose the boilerplate you mentioned emerges when adding the class constructors. This is indeed inavoidable. I would wish default constructors were generated at runtime when not declared explicitly, like this:
我想你提到的样板是在添加类构造函数时出现的。这确实是不可避免的。我希望默认构造函数在未明确声明的情况下在运行时生成,如下所示:
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
but unfortunately you have to declare them manually.
但不幸的是,您必须手动声明它们。
Edit
编辑
As Michael0x2apointed out in the comment, the need for default constructors is made avoidable in python3.7
which introduced a @dataclass
decorator, so one can indeed declare:
正如Michael0x2a在评论中指出的那样,可以避免使用默认构造函数,python3.7
其中引入了@dataclass
装饰器,因此确实可以声明:
@dataclass
class Address:
street: str
housenumber: int
housenumber_postfix: Optional[str]
@dataclass
class Person:
name: str
adresses: Sequence[Address]
and get the default impl of several methods, reducing the amount of boilerplate code. Check out PEP 557for more details.
并获得几种方法的默认实现,减少样板代码量。查看PEP 557了解更多详情。
I guess you could see stub files that can be generated from your code, as some kind of interface files:
我想您可以看到可以从您的代码生成的存根文件,作为某种接口文件:
$ stubgen spam # stubgen tool is part of mypy package
Created out/spam.pyi
The generated stub file contains the typed signatures of all non-private classes and functions of the module without implementation:
生成的存根文件包含模块的所有非私有类和函数的类型签名,没有实现:
# 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
These stub files are also recognized by IDEs and if your original module is not statically typed, they will use the stub file for type hints and code completion.
这些存根文件也被 IDE 识别,如果您的原始模块不是静态类型的,它们将使用存根文件进行类型提示和代码完成。
回答by Brian Schlenker
Python 3.6 added a new implementation of namedtuple that works with type hints, which removes some of the boilerplate required by the other answers.
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'),
],
)
Edit: NamedTuple
s are immutable, so be aware that you can't use this solution if you want to modify the fields of your objects. Changing the contents of lists
and dicts
is still fine.
编辑:NamedTuple
s 是不可变的,因此请注意,如果您想修改对象的字段,则不能使用此解决方案。更改的内容lists
,并dicts
仍然有效。
回答by Otto
A simple solution I found (that doesn't require Python 3.7) is to use SimpleNamespace:
我发现的一个简单的解决方案(不需要 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')
])
- This works in Python 3.3 and higher
- The fields are mutable (unlike NamedTuple solution)
- Code completion seems to work flawlessly in PyCharm but not 100% in VSCode (raised an issuefor that)
- Type checking in mypy works, but PyCharm does not complain if I e.g do
person.name = 1
- 这适用于 Python 3.3 及更高版本
- 字段是可变的(与 NamedTuple 解决方案不同)
- 代码完成在 PyCharm 中似乎完美无缺,但在 VSCode 中不是 100%(为此提出了一个问题)
- 在 mypy 中进行类型检查工作,但如果我这样做,PyCharm 不会抱怨
person.name = 1
If anyone can point out why Python 3.7's dataclass
decorator would be better I would love to hear.
如果有人能指出为什么 Python 3.7 的dataclass
装饰器会更好,我很乐意听到。
回答by Laurent LAPORTE
With Python 3.5, you can use annotations to specify the type of parameters and return types. Most of recent IDE, like PyCharm can interpret those annotations and give you good code completion. You can also use a comment to specify the signature of a function, or the type of a variable.
在 Python 3.5 中,您可以使用注释来指定参数的类型和返回类型。大多数最近的 IDE,如 PyCharm 可以解释这些注释并为您提供良好的代码补全。您还可以使用注释来指定函数的签名或变量的类型。
Here is an example:
下面是一个例子:
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')
])
Notice that Python is not a strongly typed language. So, annotations are only a guide for developers. If you really want to check your code, you need an external tools (currently, the best one is mypy). It can be used like any other code checker during code quality control.
请注意,Python 不是强类型语言。所以,注解只是给开发者的一个指南。如果你真的想检查你的代码,你需要一个外部工具(目前最好的是mypy)。在代码质量控制期间,它可以像任何其他代码检查器一样使用。
回答by Boris Yakubchik
Perhaps this will work well with mypy
也许这会很好地与 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},
]
Read more about TypedDictfrom the mypy docsor from the source code
从mypy 文档或源代码中阅读有关TypedDict 的更多信息
I'm pretty sure you can nest these things, and set some of them to Optional
if you'd like.
我很确定你可以嵌套这些东西,Optional
如果你愿意,可以设置其中的一些。
I got this idea from https://stackoverflow.com/a/21014863/5017391
回答by David Foster
A TypeScript interface describes a JavaScript object. Such an object is analogous to a Python dictionary with well-known string keys, which is described by a mypy TypedDict.
TypeScript 接口描述了一个 JavaScript 对象。这样的对象类似于具有众所周知的字符串键的 Python 字典,由 mypy TypedDict描述。
TypeScript interface example
TypeScript 接口示例
For example the TypeScript interface:
例如 TypeScript 接口:
interface Address {
street: string;
housenumber: number;
}
will describe JavaScript objects like:
将描述 JavaScript 对象,如:
var someAddress = {
street: 'SW Gemini Dr.',
housenumber: 9450,
};
mypy TypedDict example
mypy TypedDict 示例
The equivalent mypy TypedDict
:
等效的 mypy TypedDict
:
from typing_extensions import TypedDict
class Address(TypedDict):
street: str
housenumber: int
will describe Python dictionaries like:
将描述 Python 字典,如:
some_address = {
'street': 'SW Gemini Dr.',
'housenumber': 9450,
}
# or equivalently:
some_address = dict(
street='SW Gemini Dr.',
housenumber=9450,
)
These dictionaries can be serialized to/from JSON trivially and will conform to the analogous TypeScript interface type.
这些字典可以简单地与 JSON 序列化,并将符合类似的 TypeScript 接口类型。
Note: If you are using Python 2 or older versions of Python 3, you may need to use the older function-based syntax for TypedDict:
注意:如果您使用的是 Python 2 或更旧版本的 Python 3,您可能需要对 TypedDict 使用旧的基于函数的语法:
from mypy_extensions import TypedDict
Address = TypedDict('Address', {
'street': str,
'housenumber': int,
})
Alternatives
备择方案
There are other ways in Python to represent structures with named properties.
Python 中还有其他方法来表示具有命名属性的结构。
Named tuplesare cheap and have read-only keys. However they cannot be serialized to/from JSON automatically.
命名元组很便宜并且具有只读键。但是,它们不能自动序列化为 JSON 或从 JSON 序列化。
from typing import NamedTuple
class Address(NamedTuple):
street: str
housenumber: int
my_address = Address(
street='SW Gemini Dr.',
housenumber=9450,
)
Data classes, available in Python 3.7, have read-write keys. They also cannot be serialized to/from JSON automatically.
在 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, available in Python 3.3, are similar to data classes but are not very well known.
Python 3.3 中提供的简单命名空间类似于数据类,但不是很出名。
from types import SimpleNamespace
class Address(SimpleNamespace):
street: str
housenumber: int
my_address = Address(
street='SW Gemini Dr.',
housenumber=9450,
)
attrsis a long-standing third-party library that is similar to data classes but with many more features. attrs is recognized by the mypy typechecker.
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,
)