如何键入提示 Python 函数返回从超类派生的任何类的实例? [英] How do I type-hint that a Python function returns instance of any class derived from a superclass?

查看:34
本文介绍了如何键入提示 Python 函数返回从超类派生的任何类的实例?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一堆 Django 模板包含标签,它们将数据库对象的特定实例或 string/int 作为参数,它被解释为该数据库对象的主键.例如...

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" %}

...一切都很好,而且很明显:它们使用特定的 Product 实例呈现模板片段,如果需要,通过主键从数据库中获取它.Product 和类似的类是这样定义的:

...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}

这是 fetch_object 代码:

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]:

...然后,如果我访问获取的对象上的方法,PyCharm 会抱怨未解析的属性引用",该方法是特定于产品的,而不是特定于模型的.

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

我正在尝试在我的 Python 代码中使用越来越多的类型提示,因为它已经救了我几次,但这是其中一种情况,我不知道正确的做法是什么会是这样,我的 google-fu 让我失望了.

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.

fetch_object 的正确类型提示是什么?

What is the correct type-hinting for fetch_object?

推荐答案

你想要在这里做的是让你的 fetch_object 函数成为 通用函数.

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

也就是说,与其说您的函数接受任何 Type[Model],不如使用类型变量准确捕获您接受的模型类型,并指定确切的类型作为输出.例如:

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

关于风格约定的一个小注意事项:为了简洁起见,我选择在此处将类型变量命名为 T.另一个常见的约定是将类型变量命名为 _TModel_ModelT 之类的名称.也就是说,下划线将变量设为私有,并使用更长的名称以提高可读性.

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.

这篇关于如何键入提示 Python 函数返回从超类派生的任何类的实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆