Ist es möglich, einen Typvar von Generika als Obergrenze für einen anderen Typenvar zu definieren?Python

Python-Programme
Anonymous
 Ist es möglich, einen Typvar von Generika als Obergrenze für einen anderen Typenvar zu definieren?

Post by Anonymous »

Nehmen wir an, wir haben die folgende Klassenhierarchie: < /p>

Code: Select all

class A:
def a_method(self): ...

class B(A):
def b_method(self): ...

class C(B):
def c_method(self): ...
< /code>
und eine generische Klasse: < /p>
from typing import TypeVar, Generic

T = TypeVar('T')

class MyClass(Generic[T]):
def method(self, obj: T) -> T:
# do something
return obj
Ich möchte den .Method so eingeben, dass er genau den gleichen Typ wie das Objekt zurückgibt, das er akzeptiert, aber durch den generischen Typ der Klasse (d. H. Eine obere Grenze):

eingeschränkt, eingeschränkt,

Code: Select all

instance: MyClass[A] = MyClass()

instance.method(A())  # -> A
instance.method(B())  # -> B
instance.method(C())  # -> C

instance.method(123)  # -> Error: int is not a subtype of A
Mit der aktuellen Implementierung gehen statische Typen an, dass der Rückgabetyp immer der generische Typ t :
ist

Code: Select all

instance: MyClass[A] = MyClass()

instance.method(A())  # -> A
instance.method(B())  # -> A
instance.method(C())  # -> A
Die Frage lautet also: Können wir eine Methode so eingeben, dass sie den gleichen Typ wie das Argument zurückgibt, während wir die generische Obergrenze der Klasse immer noch durchsetzen? PrettyPrint-Override ">

Code: Select all

from typing import TypeVar, Generic, Protocol, Any

class AnyCallable(Protocol):
def __call__(self, *__args: Any, **__kwargs: Any) -> Any: ...

CallableSignaturesT = TypeVar('CallableSignaturesT', bound=AnyCallable, default=AnyCallable)

class Registry(Generic[CallableSignaturesT]):
def __init__(self) -> None:
self._registry: list[CallableSignaturesT] = []

def __call__(self, func: CallableSignaturesT) -> CallableSignaturesT:
self._registry.append(func)
return func
< /code>
Definieren wir nun ein spezifischeres Callable-Protokoll: < /p>
class MyCallable(Protocol):
def __call__(self, __arg1: int, __arg2: str, *__args: Any, **__kwargs: Any) -> Any: ...
< /code>
und erstellen Sie eine Registrierung dafür: < /p>
my_registry: Registry[MyCallable] = Registry()
Diese Registrierung verlangt, dass jedes Anruf mindestens zwei Argumente akzeptieren muss: int und str . Benutzer können auch zusätzliche Parameter definieren (die von der Anwendung basierend auf dem Parameternamen bereitgestellt werden): < /p>

Code: Select all

@my_registry
def my_callable(arg1: int, arg2: str, some_arg_from_workflow_dict: bool):
...
Derzeit akzeptiert MyPy diese Funktion. /> MyPy geht davon aus

Code: Select all

my_callable(arg1=1, arg2='str', some_arg_from_workflow_dict=True)
< /code>
-Es erlaubt nur: < /p>
my_callable(1, 'str', some_arg_from_workflow_dict=True)
Ich habe versucht, einen zweiten Typevar zu definieren.

Code: Select all

class Registry(Generic[CallableSignaturesT]):
def __init__(self) -> None:
self._registry: list[CallableSignaturesT] = []

T = TypeVar('T', bound=CallableSignaturesT)

def __call__(self, func: T) -> T:
self._registry.append(func)
return func
< /code>
MyPy berücksichtigt den Code gültig.  Aber jetzt lehnt es jedes Argument für __call __ 
:
ab.

Code: Select all

@my_registry
def my_callable(arg1: int, arg2: str, some_arg_from_workflow_dict: bool):
...

# mypy: Value of type variable "T" of "__call__" of "Registry" cannot be
# "Callable[[int, str, bool], Any]"
< /code>
def my_callable(arg1: int, arg2: str, some_arg_from_workflow_dict: bool) -> Any:
...

my_registry(my_callable)

# mypy: Value of type variable "T" of "__call__" of "Registry" cannot be
# "Callable[[int, str, bool], Any]"
# PyCharm: Expected type 'T ≤: CallableSignaturesT ≤: AnyCallable',
# got '(arg1: int, arg2: str, some_arg_from_workflow_dict: bool) -> Any' instead

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post