Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type Hints Convention for Instance Variables Python

Tags:

I'm unsure of the Python convention for type hinting instance variables - I've been doing them within the __init__ constructor arguments like seen here:

class LoggedVar(Generic[T]):
    def __init__(self, value: T, name: str, logger: Logger) -> None:
        self.name = name
        self.logger = logger
        self.value = value`

But I also see the PEP conventions of annotating instance variables as such (snippet below) and then also doing type hinting within the __init__ arguments:

class BasicStarship:
    captain: str = 'Picard'               # instance variable with default
    damage: int                           # instance variable without default
    stats: ClassVar[Dict[str, int]] = {}  # class variable`

    def __init__(self, damage: int, captain: str = None):
        self.damage = damage
        if captain:
            self.captain = captain  # Else keep the default

Lastly, later on in the PEP 526 article they say one can do the following for convenience and convention:

class Box(Generic[T]):
    def __init__(self, content):
        self.content: T = content

(Both of the above code snippets are from here.)

So — is one of these conventions better/more widely accepted than the others that I should try to stick to (better readability etc..)?

like image 966
hhprogram Avatar asked Jul 06 '17 22:07

hhprogram


People also ask

How do you type hints in Python?

Here's how you can add type hints to our function: Add a colon and a data type after each function parameter. Add an arrow ( -> ) and a data type after the function to specify the return data type.

Does Python have type hints?

Python's type hints provide you with optional static typing to leverage the best of both static and dynamic typing. Besides the str type, you can use other built-in types such as int , float , bool , and bytes for type hintings. To check the syntax for type hints, you need to use a static type checker tool.

Does Python enforce type hints?

The Python runtime does not enforce function and variable type annotations. They can be used by third party tools such as type checkers, IDEs, linters, etc. This module provides runtime support for type hints. The most fundamental support consists of the types Any , Union , Callable , TypeVar , and Generic .

How do you check the type of a variable in Python?

To get the type of a variable in Python, you can use the built-in type() function. In Python, everything is an object. So, when you use the type() function to print the type of the value stored in a variable to the console, it returns the class type of the object.


2 Answers

I would recommend using the first version, where you assign types to your __init__ method's parameters, for most circumstances.

That particular method has the least amount of redundancy while still allowing type checkers to verify that you're calling that __init__ method correctly elsewhere in your code.

I would recommend using either the second or third version, where you explicitly annotate your fields (inside or outside __init__) when your __init__ method has grown complex enough to the point where one or more of the following apply:

  1. It's no longer so straightforward what exactly your fields are to begin with
  2. There's no longer a one-to-one mapping between your parameters and your fields
  3. You have complex initialization logic that obscures how your fields are being assigned.

However, it was unclear to me whether the second or third version was preferred -- I personally prefer the third version because it's more conceptually cleaner and doesn't seem to mix the notion of instance vs class attributes, but I can't deny the second version looks cleaner.

I asked about it on the 'typing' gitter channel, and got the following response from Guido (who, on the off-chance you didn't know, made Python and is currently working on mypy and typing related stuff):

There seem to be strong opinions either way. I do indeed prefer putting attribute annotations in the class body rather than sprinkling them throughout __init__ and other methods. I also think that with PEP 526 this will be the future (also with things like class-based NamedTuple declarations and possibly https://github.com/ericvsmith/dataclasses).

(link to quote)

So, it seems like the second version is recommended over the third, and that defining classes in that manner will become more deeply integrated into the Python language itself at some point in the future!

Edit: PEP 557, data classes was recently accepted and appears to be on-track (?) to be included with Python 3.7.

like image 157
Michael0x2a Avatar answered Sep 19 '22 14:09

Michael0x2a


@Asara

It seems that as of Python 3.8.10 / Mypy 0.910 (Sep 2021), when it comes to distinguishing between a type annotation for an instance variable in a class definition and a declaration for a class (static) variable in a class definition, the assignment of a default value makes all the difference. If you do not assign a default (e.g., x: int), Python treats the expression as a type annotation; if you assign a default (e.g., x: int = 42), Python treats the expression as a class (static) variable declaration.

It is possible to create a type annotation for a class (static) variable in a class definition, using the ClassVar syntax. If you do not assign a default (e.g., y: ClassVar[int]), a factual class (static) variable will not be created; if you assign a default (e.g., y: ClassVar[int] = 69), a factual class (static) variable will be created.

like image 32
Gary Avatar answered Sep 22 '22 14:09

Gary