Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python type hint for (any) class

I want to type hint the following function:

def get_obj_class(self) -> CLASS_TYPE:
  return self.o.__class__
  • CLASS_TYPE should denote classes.
  • self.o could be of any type determined at runtime.

On a similar note, if I have a function f(cls: CLASS_TYPE) which returns an instance of cls, is there a way to type hint the return value appropriately?

like image 335
yspreen Avatar asked Jun 02 '17 09:06

yspreen


People also ask

How do you write a hint class in Python?

In a type hint, if we specify a type (class), then we mark the variable as containing an instance of that type. To specify that a variable instead contains a type, we need to use type[Cls] (or the old syntax typing. Type ). We need to add type hints for make_animal() .

Do type hints do anything Python?

Type hints help you build and maintain a cleaner architecture. The act of writing type hints forces you to think about the types in your program. While the dynamic nature of Python is one of its great assets, being conscious about relying on duck typing, overloaded methods, or multiple return types is a good thing.

What does type () do in Python?

Python has a lot of built-in functions. The type() function is used to get the type of an object. When a single argument is passed to the type() function, it returns the type of the object. Its value is the same as the object.

What is any type in Python?

The purpose of the Any type is to indicate to the type checker that a part of the program should not be checked. A variable (or function parameter) that is annotated with the Any type accepts any value, and the type checker allows any operation on it.


3 Answers

I'd recommend using a combination of TypeVar, to indicate that your self.o value could be any arbitrary type, and Type, in the following way:

from typing import TypeVar, Type

T = TypeVar('T')

class MyObj:
    def __init__(self, o: T) -> None:
        self.o = o

    def get_obj_class(self) -> Type[T]:
        return type(self.o)

def accept_int_class(x: Type[int]) -> None:
    pass

i = MyObj(3)
foo = i.get_obj_class()
accept_int_class(foo)    # Passes

s = MyObj("foo")
bar = s.get_obj_class()
accept_int_class(bar)    # Fails

If you want the type of o to be even more dynamic, you could explicitly or implicitly give it a type of Any.


Regarding your latter question, you'd do:

def f(cls: Type[T]) -> T:
    return cls()

Note that you need to be careful when instantiating your class -- I don't remember what Pycharm does here, but I do know that mypy currently does not check to make sure you're calling your __init__ function correctly/with the right number of params.

(This is because T could be anything, but there's no way to hint what the constructor ought to look like, so performing this check would end up being either impossibly or highly difficult.)

like image 102
Michael0x2a Avatar answered Oct 04 '22 06:10

Michael0x2a


For Python >=3.7, use type (see also PEP 585):

def get_obj_class(self) -> type:
    return self.o.__class__

For Python <3.7, use typing.Type:

def get_obj_class(self) -> typing.Type:
    return self.o.__class__
like image 23
VMRuiz Avatar answered Oct 04 '22 05:10

VMRuiz


What about typing.Type?

That seems to fit since __class__ should always return a type.

import typing

def test(t: object) -> typing.Type:
    return t.__class__

class Dummy(object):
    pass

test(Dummy())

To your second question: that should be a generic.

like image 42
Christian Sauer Avatar answered Oct 04 '22 05:10

Christian Sauer