2

I'm having some issues figuring out how to mark up functions for type checking. Let's say I have the following situation:

class BaseClass:
    def __init__(self):
        self.name = 'base'


class MainClass(BaseClass):
    def __init__(self):
        self.mc_val = 1
        super().__init__()


def base_func(bc: BaseClass) -> BaseClass:
    bc.name = 'altered'
    return bc


def main_func(mc: MainClass) -> MainClass:
    mc.mc_val = 3
    return mc

mc = MainClass()
mc1 = base_func(mc)
mc2 = main_func(mc1)

This raises an issue in my type checker (Pycharm) because I've told it that mc1 is an instance of BaseClass, rather than MainClass, and so it doesn't fit the argument type for main_func.

So I need base_func to take an argument of type BaseClass (incl subclasses), but to return not BaseClass but the same type as was originally entered. I could have BaseClass both take and return, say, TypeVar('T') or similar, but then this rather defeats the point of type checking in the first place.

Any help greatly appreciated!

4
  • I think Python requires the type hints to be evaluated at compile time, not call-time. So you can't specify that the return type is dependent on an argument type. Commented Jan 14, 2020 at 3:49
  • @Barmar - thanks - I was wondering whether something like that might be the case myself. But I can, say, specify the return type of base_func as Union[BaseClass, MainClass], which removes the type error. So presumably there is some way to achieve the same result by less janky means. Commented Jan 14, 2020 at 4:05
  • That doesn't generalize well, since you'll have to update base_func whenever you add a new subclass. Also, if you write bc1 = base_func(bc), Pycharm will let you call main_func(bc1) Commented Jan 14, 2020 at 4:09
  • @Barmar you can, Python's typing system is rich enough to express something like that Commented Jan 14, 2020 at 4:10

1 Answer 1

4

You probably want a bounded type variable:

import typing
class BaseClass:
    def __init__(self):
        self.name = 'base'


class MainClass(BaseClass):
    def __init__(self):
        self.mc_val = 1
        super().__init__()

T = typing.TypeVar('T', bound=BaseClass)
def base_func(bc: T) -> T:
    bc.name = 'altered'
    return bc


def main_func(mc: MainClass) -> MainClass:
    mc.mc_val = 3
    return mc

mc = MainClass()
mc1 = base_func(mc)
mc2 = main_func(mc1)

Using mypy:

juan$ mypy test_typing.py
Success: no issues found in 1 source file
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.