4 votes

__instancecheck__ - l'écrasement n'a aucun effet - qu'est-ce que je fais mal ?

J'essaie de faire en sorte que ma classe apparaisse comme un objet différent pour contourner la vérification paresseuse des types dans un paquet que j'utilise. Plus précisément, j'essaie de faire en sorte que mon objet apparaisse comme une instance d'un autre objet ( tuple dans mon cas) alors qu'en réalité il ne s'agit même pas d'une dérivation de celle-ci.

Pour ce faire, je prévois d'écraser le fichier __isinstance__ qui, selon la docs devrait faire exactement ce que je désire. Cependant, il semble que je n'ai pas compris comment faire exactement, car mes tentatives ont été infructueuses.

Voici un SSCCE qui devrait faire isinstance retourner False dans tous les cas, mais ne le fait pas.

class FalseInstance(type):
    def __instancecheck__(self, instance):
        return False

class Foo(metaclass=FalseInstance):
    pass

g = Foo()
isinstance(g, Foo)
> True

Qu'est-ce que je fais de mal ?

7voto

user2357112 Points 37737

Mis à part les problèmes avec __metaclass__ et le chemin rapide pour une correspondance exacte de type, __instancecheck__ fonctionne dans le sens inverse de ce que vous essayez de faire. Une classe __instancecheck__ vérifie si les autres objets sont considérés comme des instances virtuelles de cette classe, et non si les instances de cette classe sont considérées comme des instances virtuelles d'autres classes.

Si vous voulez que vos objets mentent au sujet de leur type en isinstance (vous ne devriez vraiment pas), alors la façon de le faire est de mentir au sujet de __class__ , ne pas mettre en œuvre __instancecheck__ .

class BadIdea(object):
    @property
    def __class__(self):
        return tuple

print(isinstance(BadIdea(), tuple)) # prints True

Au fait, si vous voulez obtenir le type réel d'un objet, utilisez type plutôt que de vérifier __class__ o isinstance .

5voto

DeepSpace Points 31729

Si vous ajoutez une impression à l'intérieur FalseInstance.__instancecheck__ vous verrez qu'il n'est même pas invoqué. Cependant, si vous appelez isinstance('str', Foo) alors vous verrez que FalseInstance.__instancecheck__ est invoqué.

Ceci est dû à une optimisation dans isinstance qui renvoie immédiatement 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;
    .
    .
    .
}

Extrait du code source de Python

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X