synic
synic

Reputation: 26678

Python type hinting: return type based on attribute

Trying out type hinting, and I'm having a bit of trouble wrapping my head around something:

class Node(object):
    credentials_class = credentials.BaseCredentials

    @property
    def credentials_instance(self) -> [credentials_class]:
        return self.credentials_class(self.credentials_data)


class OtherNode(Node):
    credentials_class = credentials.OtherCredentials  # subclass of BaseCredentials    

How can I make it so that OtherNode().credentials_instance returns the type that is specified in credentials_class, for all subclasses of Node?

The credentials_class attribute is not required, if there is some other way for me to tell the system that "this node's credentials_instance property returns this instance type", that would be fine.

Upvotes: 3

Views: 1182

Answers (1)

Slavfox
Slavfox

Reputation: 89

The answer is generics. By making Node generic and parametrizing with the credentials class, you can specify the types as you want them to work. The downside is, the topmost class cannot assign a default value to credentials_class, because it'd conflict with it being generic.

from typing import Type, Generic, TypeVar

CredentialsClassT = TypeVar("CredentialsClassT", bound=credentials.BaseCredentials)

class Node(Generic[CredentialsClassT]):
    credentials_class: Type[CredentialsClassT]

    @property
    def credentials_instance(self) -> CredentialsClassT:
        return self.credentials_class(self.credentials_data)

# example usage
class BaseNode(Node[credentials.BaseCredentials]):
    credentials_class = credentials.BaseCredentials

class OtherNode(Node[credentials.OtherCredentials]):
    credentials_class = credentials.OtherCredentials  # subclass of BaseCredentials    

With that typing in place, eg.

reveal_type(OtherNode.credentials_instance)

will make MyPy print not: Revealed type is 'credentials.OtherCredentials' as expected.

Upvotes: 3

Related Questions