如何基于传入的实例上方法的返回值在Python中编写一个函数?

huangapple go评论71阅读模式
英文:

How do type a function in python based on the return value of a method on an instance passed in?

问题

在Python中,您可以使用继承和多态来实现类似的功能。以下是您示例的Python版本:

class DocumentSuperClass:
    pass

class DocumentA(DocumentSuperClass):
    pass

class DocumentB(DocumentSuperClass):
    pass
# ... many more sibling classes

class SuperClass:
    def __init__(self, doc):
        self.doc = doc

    def meth(self):
        return self.doc

class A(SuperClass):
    pass

class B(SuperClass):
    pass
# ... many more sibling classes

def a(input):
    return input.meth()

instanceOfDocumentA = a(A(DocumentA()))
instanceOfDocumentB = a(B(DocumentB()))
# ... same behavior for many more sibling classes

这段代码实现了与 TypeScript 示例类似的功能,根据传递给函数的实例的行为返回不同的值。

英文:

In TypeScript, I can define a function such that the return value is different depending on the behavior of an instance that was passed into the function. (See the example below.)

How can I do something similar in Python?

class DocumentSuperClass {
   

}
class DocumentA extends DocumentSuperClass {}
class DocumentB extends DocumentSuperClass {}
// ... many more sibling classes

class SuperClass {
    doc: DocumentSuperClass

    meth() {
        return this.doc;
    }
}

class A {
    doc: DocumentA

    meth() {
        return this.doc;
    }
}

class B {
    doc: DocumentB

    meth() {
        return this.doc;
    }
}
// ... many more sibling classes

function a<T extends SuperClass>(input: T) {
    return input.meth() as ReturnType<T['meth']>;
}

const instanceOfDocumentA = a(new A());
const instanceOfDocumentB = a(new B());
// ... same behavior for many more sibling classes

答案1

得分: 1

以下是翻译好的部分:

你有几个选择:

使用 @overload

from dataclasses import dataclass
from typing import overload

@dataclass
class A:
    val: int

@dataclass
class B:
    val: str


@overload
def a(arg: A) -> int: ...
@overload
def a(arg: B) -> str: ...

def a(arg: A | B):
    return arg.val


typed_as_int = a(A(123))
typed_as_str = a(B("abc"))

返回类型取决于传入的参数:

Foo().one()         # -> Tuple[Any, ...] | None
Foo().one('tuple')  # -> Tuple[Any, ...] | None
Foo().one('dict')   # -> Dict[str, Any] | None

使用泛型

from typing import TypeVar

T = TypeVar('T')

def foo(arg: T) -> T:
    return arg

foo(1)      # -> int
foo('bar')  # -> str

另一种替代方案:

可以使用 Union[] 类型,但这会使您的函数返回联合类型,而不是实际的运行时类型。

英文:

You've got a few options:

Use @overload

from dataclasses import dataclass
from typing import overload

@dataclass
class A:
    val: int

@dataclass
class B:
    val: str


@overload
def a(arg: A) -> int: ...
@overload
def a(arg: B) -> str: ...

def a(arg: A | B):
    return arg.val


typed_as_int = a(A(123))
typed_as_str = a(B("abc"))

The return types depend on what's passed in:

Foo().one()         # -> Tuple[Any, ...] | None
Foo().one('tuple')  # -> Tuple[Any, ...] | None
Foo().one('dict')   # -> Dict[str, Any] | None

Use generics

from typing import TypeVar

T = TypeVar('T')

def foo(arg: T) -> T:
    return arg

foo(1)      # -> int
foo('bar')  # -> str

another alternative:

Could use Union[] type, but that makes your function return the union type as well, not the actual runtime type.

答案2

得分: 0

你可以使用'singledispatch'用于函数,或者'singledispatchmethod'用于类方法。

此外,此代码通过了mypy检查,所以代码的类型注解是正确的。

from functools import singledispatch

class A:
    def meth(self):
        return 'a'
    
class B:
    def meth(self):
        return 1
    
C = A | B

@singledispatch
def function(input_: C):
    pass
@function.register
def _(input_: A) -> str:
    return input_.meth()
@function.register
def _(input_: B) -> int:
    return input_.meth()

print(function(A()))
print(function(B()))
a
1
英文:

You can use 'singledispatch' for functions or 'singledispatchmethod' for class methods.

Also, this code passes mypy checks, so code's typing annotations are fine.

from functools import singledispatch

class A:
    def meth(self):
        return 'a'
    
class B:
    def meth(self):
        return 1
    
C = A | B

@singledispatch
def function(input_: C):
    pass
@function.register
def _(input_: A) -> str:
    return input_.meth()
@function.register
def _(input_: B) -> int:
    return input_.meth()

print(function(A()))
print(function(B()))
a
1

答案3

得分: 0

这似乎有效。对我来说,奇怪的是我没有明确定义返回类型。但如果我只是将其未定义,类型提示器似乎能够解决问题:

class DocumentSuperClass:
    pass

class DocumentA(DocumentSuperClass):
    pass

class DocumentB(DocumentSuperClass):
    pass

class SuperClass:
    def __init__(self):
        self.doc = DocumentSuperClass

    def meth(self):
        return self.doc

class ClassA(SuperClass):
    def __init__(self):
        self.doc = DocumentA()

    def meth(self):
        return self.doc

class ClassB(SuperClass):
    def __init__(self):
        self.doc = DocumentB()

    def meth(self):
        return self.doc

def func(input: SuperClass):
    return input.meth()

instanceOfDocumentA = func(ClassA())
instanceOfDocumentB = func(ClassB())
英文:

This seemed to work. It's odd to me that I'm not defining the return type explicitly. But if I just leave it undefined, the type hinter seems to figure things out:

class DocumentSuperClass:
    pass

class DocumentA(DocumentSuperClass):
    pass

class DocumentB(DocumentSuperClass):
    pass

class SuperClass:
    def __init__(self):
        self.doc = DocumentSuperClass

    def meth(self):
        return self.doc

class ClassA(SuperClass):
    def __init__(self):
        self.doc = DocumentA()

    def meth(self):
        return self.doc

class ClassB(SuperClass):
    def __init__(self):
        self.doc = DocumentB()

    def meth(self):
        return self.doc

def func(input:SuperClass):
    return input.meth()


instanceOfDocumentA = func(ClassA())
instanceOfDocumentB = func(ClassB())

huangapple
  • 本文由 发表于 2023年5月18日 01:13:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/76274619.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定