Subclase en sugerencias de tipo

Resuelto user1211030 asked hace 7 años • 3 respuestas

Quiero permitir sugerencias de tipos usando Python 3 para aceptar subclases de una determinada clase. P.ej:

class A:
    pass

class B(A):
    pass

class C(A):
    pass

def process_any_subclass_type_of_A(cls: A):
    if cls == B:
        # do something
    elif cls == C:
        # do something else

Ahora al escribir el siguiente código:

process_any_subclass_type_of_A(B)

Recibo una sugerencia de PyCharm IDE: "Se esperaba el tipo A, en su lugar obtuve el tipo [B]".

¿Cómo puedo cambiar las sugerencias de tipo aquí para aceptar cualquier subtipo de A?

Según PEP 484 ("Las expresiones cuyo tipo es un subtipo de un tipo de argumento específico también se aceptan para ese argumento"), ¿entiendo que mi solución (cls: A)debería funcionar?

user1211030 avatar Sep 07 '17 15:09 user1211030
Aceptado

Cuando especifica cls: A, está diciendo que clsespera una instancia de tipo A. La sugerencia de tipo para especificar clscomo objeto de clase para el tipo A(o sus subtipos) utiliza typing.Type.

from typing import Type
def process_any_subclass_type_of_A(cls: Type[A]):
    pass

Del tipo de objetos de clase :

A veces quieres hablar de objetos de clase que heredan de una clase determinada. Esto se puede escribir como Type[C]¿dónde Chay una clase? En otras palabras, cuando Ces el nombre de una clase, usarlo Cpara anotar un argumento declara que el argumento es una instancia de C(o de una subclase de C), pero usar Type[C]como argumento la anotación declara que el argumento es un objeto de clase que se deriva de C( o a Csí mismo).

Ashwini Chaudhary avatar Sep 07 '2017 09:09 Ashwini Chaudhary

Si miramos la Typedescripción del typingmódulo, vemos estos documentos:

Una construcción especial que se puede utilizar para anotar objetos de clase.

Por ejemplo, supongamos que tenemos las siguientes clases::

 class User: ...  # Abstract base for User classes
 class BasicUser(User): ...
 class ProUser(User): ...
 class TeamUser(User): ...

Y una función que toma un argumento de clase que es una subclase de Usuario y devuelve una instancia de la clase correspondiente::

 U = TypeVar('U', bound=User)
 def new_user(user_class: Type[U]) -> U:
     user = user_class()
     # (Here we could write the user object to a database)
     return user

 joe = new_user(BasicUser)

En este punto, el verificador de tipos sabe que Joe tiene el tipo BasicUser.

En base a esto, puedo imaginar un ejemplo sintético que reproduce el problema con los errores de sugerencia de tipos en PyCharm.

from typing import Type, Tuple

class BaseClass: ...

class SubClass(BaseClass): ...
class SubSubClass(SubClass): ...

def process(model_instance: BaseClass, model_class: Type[BaseClass]) -> Tuple[BaseClass, BaseClass]:
    """ Accepts all of the above classes """
    return model_instance, model_class()


class ProcessorA:
    @staticmethod
    def proc() -> Tuple[SubClass, SubClass]:
        """ PyCharm will show an error 
        `Expected type 'tuple[SubClass, SubClass]', got 'tuple[BaseClass, BaseClass]' instead` """
        return process(SubClass(), SubClass)


class ProcessorB:
    @staticmethod
    def proc() -> Tuple[SubSubClass, SubSubClass]:
        """ PyCharm will show an error 
        `Expected type 'tuple[SubSubClass, SubSubClass]', got 'tuple[BaseClass, BaseClass]' instead` """
        return process(SubSubClass(), SubSubClass)

Pero vemos en los documentos Typeque la situación se puede corregir usando TypeVarel boundargumento. Luego úselo en lugares donde BaseClassesté declarado como tipo.

from typing import TypeVar, Type, Tuple

class BaseClass: ...

B = TypeVar('B', bound=BaseClass)

class SubClass(BaseClass): ...
class SubSubClass(SubClass): ...

def process(model_instance: B, model_class: Type[B]) -> Tuple[B, B]:
    """ Accepts all of the above classes """
    return model_instance, model_class()


class ProcessorA:
    @staticmethod
    def proc() -> Tuple[SubClass, SubClass]:
        return process(SubClass(), SubClass)


class ProcessorB:
    @staticmethod
    def proc() -> Tuple[SubSubClass, SubSubClass]:
        return process(SubSubClass(), SubSubClass)

Esperamos que esto sea útil.

FRiMN avatar Mar 11 '2022 16:03 FRiMN

El tipo [A] acepta también la clase misma, que no siempre es necesaria.

Si desea que su función acepte solo subclases, debe utilizar NewType , como

class A:
    pass

B = NewType('B', A)

def foo(cls: Type[B]):
   ...
BouygudSt avatar Jul 31 '2022 17:07 BouygudSt