Yuval Pruss
Yuval Pruss

Reputation: 9816

Type annotation with multiple types in **kwargs

I'm trying out using Python's type annotations with abstract class. My __init__ function looks like this:

from abc import ABCMeta

class SomeClass(object, metaclass=ABCMeta):
    def __init__(self, *args, **kwargs):
        print("Initiating %s object.", self.__class__.__name__)

        self.username = kwargs['data']
        assert isinstance(self.username, str)

        is_premioum = kwargs.get('premioum', False)

        self.money_investmant = kwargs.get('investmant')
        if isinstance(self.money_investmant, str):
            self.money_investmant = float(self.money_investmant)

As you can see, kwargs could contain arguments from a several number of types- float, bool and str.

Now, I am trying to write the type annotation for the function, that looks like this:

def __init__(self, *args, **kwargs: Union[bool, str, float]) -> None:

But my PyCharm IDE alerts me:

Except type 'Integral', got 'str' instead

And:

Cannot find referance 'get' in bool | str | float'

Am I doing something wrong?

How should I write the type annotation for kwargs if it contains arguments from multiple types?

Upvotes: 10

Views: 11765

Answers (2)

spacether
spacether

Reputation: 2699

If one wants to describe specific named arguments expected in kwargs, one can instead pass in a TypedDict which defines required and optional parameters. Optional parameters are what were the kwargs:

This allows one to have unset (NOT None default) optional arguments AND have type hints on them.

This is also useful if they keys have invalid python variable names because TypedDict is the only way to define the types of those values aside from the very general **kwargs value type hinting.

import typing
from abc import ABCMeta


class RequiredProps(typing.TypedDict):
    # all of these must be present
    data: str

class OptionalProps(typing.TypedDict, total=False):
    # these can be included or they can be omitted
    premium: bool
    investment: typing.Union[str, float]

class ReqAndOptional(RequiredProps, OptionalProps):
    pass

class SomeClass(object, metaclass=ABCMeta):
    def __init__(self, *args, kwargs: ReqAndOptional):
        print("Initiating %s object.", self.__class__.__name__)

        self.username = kwargs['data']
        assert isinstance(self.username, str)

        is_premium = kwargs.get('premium', False)
        assert isinstance(is_premium, bool)

        self.money_investment = kwargs.get('investment')
        assert isinstance(elf.money_investment, (str, float))
        if isinstance(self.money_investment, str):
            self.money_investment = float(self.money_investment)

Upvotes: 2

Dimitris Fasarakis Hilliard
Dimitris Fasarakis Hilliard

Reputation: 160377

See this bug and this bug on the issue tracker for PyCharm. This is apparently an issue with PyCharm's checker; mypy (another type checker for Python) does not complain when I execute similar code.

There's already a fix for this and, it's apparently available in build 171.2014.23. Until then, I'd suppose Any would suffice as a temporary workaround to get the checker to stop complaining.

Upvotes: 4

Related Questions