Search code examples
pythonpython-typing

Annotate a function return type with an instance, not a type


I would like to type a function to an actual object, not the type of this object, and the type checker to understand this.

Imagine this example:

my_marker = "This is very singletony, I promise!"

def get_object_or_a_singleton_marker() -> int|my_marker: # invalid syntax
  if (some_condition_is_met):
    return some_int_value() 
  else:
    return my_marker # singleton

a_var = get_object_or_a_singleton_marker()

if a_var is not my_marker:
   # I want the type to be narrowed here

For the record, here the singleton is a string, but in my real code, it would be a class instance.

The example as written is invalid, because my_marker is not a type expression. Using Literal[my_marker] has the same issue.

If I use type[my_marker] as annotation, then the narrowing will not work (in my example, the variable will not be my_marker but could be any other str)

I could probably use a TypeGuard function, but the idea is to keep the syntax as is, ie. with the use of is, without any other boilerplate.

Can I annotate a function with an instance, not a type?


Solution

  • One workaround would be to make the singleton object the value of an Enum, so you can type a function with it in a typing.Literal:

    from typing import Literal
    from enum import Enum
    from random import random
    
    class MyMarker(Enum):
        MARKER = "This is very singletony, I promise!"
    
    def get_object_or_a_singleton_marker() -> int | Literal[MyMarker.MARKER]:
        if random() < 0.5:
            return 1
        else:
            return MyMarker.my_marker
    
    a_var = get_object_or_a_singleton_marker()
    
    if a_var is not MyMarker.MARKER:
        pass
    

    Demo: https://mypy-play.net/?mypy=latest&python=3.12&gist=76062ce158f1d56a3d8c996209ad5a7e