Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python equivalent of Typescript interface

Recently I have been working with Typescript a lot, it allows to express things like:

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.

How is this done in 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).

like image 707
Otto Avatar asked Jan 14 '18 21:01

Otto


People also ask

Is there anything like TypeScript for Python?

Unlike TypeScript, typing comes built into Python. The common types (str, float, int, bool, dict, list) are always available and the other types (e.g. Tuple, Union, Optional) are imported from the standard module typing.

Why is there no TypeScript in Python?

Python doesn't have this issue because coercions between types are specified at the type level rather than the interpreter shrugging at some nonsense and moving on. Now, I will give you that typescript will catch your nonsense at compile time (well, sometimes) and Python will readily run until it blows up.

Should I use interface or type in TypeScript?

Interfaces are most recommended for defining new objects or methods or properties of an object where it will receive a specific component. Hence interface works better when using objects and method objects. Therefore it is our choice to choose between types or interface according to the program needs.

What is TypeScript interface?

Interface is a structure that defines the contract in your application. It defines the syntax for classes to follow. Classes that are derived from an interface must follow the structure provided by their interface. The TypeScript compiler does not convert interface to JavaScript.


1 Answers

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:

# 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 Michael0x2a pointed 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:

@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 557 for more details.


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.

like image 189
hoefling Avatar answered Sep 18 '22 22:09

hoefling