__instancecheck__ – sobrescribir no muestra ningún efecto, ¿qué estoy haciendo mal?

Estoy tratando de hacer que mi clase aparezca como un objeto diferente para evitar la comprobación de tipos perezosos en un paquete que estoy usando. Más específicamente, estoy tratando de hacer que mi objeto aparezca como una instancia de otro objeto ( tuple en mi caso) cuando en realidad ni siquiera es una derivación de eso.

Para lograr esto, planeo sobrescribir el método __isinstance__ que, según los documentos , debe hacer exactamente lo que deseo. Sin embargo, parece que no entendí cómo hacer eso exactamente, porque mis bashs no han tenido éxito.

Aquí hay un SSCCE que debería hacer que el retorno de la isinstance False en todos los casos, pero no lo hace.

 class FalseInstance(type): def __instancecheck__(self, instance): return False class Foo(metaclass=FalseInstance): pass g = Foo() isinstance(g, Foo) > True 

¿Qué estoy haciendo mal?

Aparte de los problemas con __metaclass__ y la ruta rápida para una coincidencia de tipo exacta, __instancecheck__ funciona en la dirección opuesta a la que intentas hacer. El __instancecheck__ una clase verifica si otros objetos se consideran instancias virtuales de esa clase, no si las instancias de esa clase se consideran instancias virtuales de otras clases.

Si desea que sus objetos mientan sobre su tipo en isinstance verificaciones de isinstance (realmente no debería), entonces la forma de hacerlo es mentir sobre __class__ , no implementar __instancecheck__ .

 class BadIdea(object): @property def __class__(self): return tuple print(isinstance(BadIdea(), tuple)) # prints True 

Incidentalmente, si desea obtener el tipo real de un objeto, use el type lugar de marcar __class__ o isinstance .

Si agrega una impresión dentro de FalseInstance.__instancecheck__ verá que ni siquiera se invoca. Sin embargo, si llama a isinstance('str', Foo) , verá que se FalseInstance.__instancecheck__ .

Esto se debe a una optimización en la implementación de isinstance que devuelve inmediatamente True si type(obj) == given_class :

 int PyObject_IsInstance(PyObject *inst, PyObject *cls) { _Py_IDENTIFIER(__instancecheck__); PyObject *checker; /* Quick test for an exact match */ if (Py_TYPE(inst) == (PyTypeObject *)cls) return 1; . . . } 

Desde el código fuente de Python