How do I type-hint that a Python function returns instance of any class derived from a superclass?

Elver Loho

I've got a bunch of Django template inclusion tags, which take as an argument either a specific instance of a database object or a string/int, which is interpreted as the primary key of that database object. For example...

{% render_product product=obj %}
{% render_product product=42 %}
{% render_product product="42" %}

...all work fine and do the obvious: they render a template fragment with a particular Product instance, fetching it by primary key from the database, if needed. This is how Product and similar classes are defined:

class Product(models.Model):
    # standard django model definition goes here

Here's what usually happens in such an inclusion tag:

@register.inclusion_tag("render_product.html")
def render_product(product: Union[Product, str, int] = None) -> dict:
    _product = None
    if isinstance(product, Product):
        _product = product
    elif isinstance(product, str) or isinstance(product, int):
        try:
            _product = Product.objects.get(pk=product)
        except (Product.DoesNotExist, ValueError):
            pass
    return {"product": _product}

Since I've got the same pattern happening in dozens of inclusion tags, I'm trying to refactor it out, so that I've got something like:

@register.inclusion_tag("render_product.html")
def render_product(product: Union[Product, str, int] = None) -> dict:
    _product = fetch_object(Product, product)
    return {"product": _product}

Here's the fetch_object code:

def fetch_object(cls: Type[Model] = None, obj: Union[Model, str, int] = None):
    if isinstance(obj, cls):
        return obj
    elif isinstance(obj, str) or isinstance(obj, int):
        try:
            return cls.objects.get(pk=obj)
        except (cls.DoesNotExist, ValueError):
            pass
    return None

My problem is: I have no idea how to specify the return type of that function. Basically it should be something like "instance of any class, which is derived from Model or None". But if I try something like...

def fetch_object(
    cls: Type[Model] = None, obj: Union[Model, str, int] = None
) -> Union[Model, None]:

...then PyCharm complains about "unresolved attribute reference" if I access a method on the fetched object, which is Product-specific, not Model-specific.

I'm trying to use more and more type-hinting in my Python code, because it has already saved my butt a few times, but this is one of those cases, where I have no idea what the correct way of doing it would be and my google-fu is failing me.

What is the correct type-hinting for fetch_object?

Michael0x2a

What you want to do here is make your fetch_object function a generic function.

That is, rather then just saying that your function accepts any Type[Model], capture exactly which kind of model you accept using a type variable, and specify that exact kind is the output. For example:

from typing import TypeVar

# The bound states that T can be bound to Model or any subclass of Model.
# If the bound keyword argument is omitted, we assume the bound is 'object'.
T = TypeVar('T', bound=Model)

def fetch_object(cls: Type[T] = None, obj: Union[T, str, int] = None) -> Optional[T]:
    if isinstance(obj, cls):
        return obj
    elif isinstance(obj, str) or isinstance(obj, int):
        try:
            return cls.objects.get(pk=obj)
        except (cls.DoesNotExist, ValueError):
            pass
    return None

One minor note on stylistic conventions: I chose to name the typevar T here for brevity. The other common convention is to name your typevar something like _TModel or _ModelT. That is, the underscore to make the variable private, and a longer name for readability.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How do I type hint a method with the type of the enclosing class?

How do I type hint a function that returns an instance of the current class?

In Python 3.5, how can I specify a function as a type hint?

How do I declare an array of any type (derived from a given type) in an abstract class?

How to type hint a function that returns a function?

Python type hint for (any) class

C++ How can I call superclass constructor with two parameters from derived class constructor with one parameter?

How to hint base class function that returns an instance of derived class it is called from?

How do I type hint a filename in a function?

I've derived a class from the list type. But index slicing on my class returns a Python list object, instead of my own object

How can I call parent class overloaded function from derived class instance?

C# How do I get all the fields of a specific type from a derived class within the base class?

How do I annotate the type of a parameter of an abstractmethod, when the parameter can have any type derived from a specific base type?

In Python, how do I type hint 'has attribute'?

How do I design the Storage class such that it accommodates any storage type and at the same time there is only one instance of each?

How do I call a base class method from within the same overloaded derived class method in python?

How do I access an inner class constructor from a derived class?

How do I simulate returns from an empirically derived distribution in MATLAB (Or Python)?

How do I create a variable accessible by any function in a class in python?

How to type hint that a function returns another function?

Python type annotations for instance of class derived from abstract base class

How to hint the type of a function I do not control?

Python - how to type hint calling a function that returns a tuple?

How do I use type hint for multiple classes in a dictionary but they inherit from same parent class?

How do I type hint a question that returns a zip object?

How do I take input from the the sub class here? When I run this code it uses the values from superclass in Move() Function

python instance function do not find type(class)

How to provide type hint for a function that returns an Protocol subclass in Python?

How may I instantiate a class of type hint in python?