10
votes

issubclass () renvoie false sur la même classe importée de différents chemins

L'intention était de mettre en œuvre une sorte de cadre de plug-in, où les plugins sont des sous-classes (c'est-à-dire B) de la même classe de base (c'est-à-dire). La classe de base est chargée d'importation standard, tandis que les sous-classes sont chargées avec imp.load_module () du trajet d'un emballage bien connu (c.-à-d. PKG).

# test_2.py
import pkg
from pkg import mod1
import imp
tup = imp.find_module('mod1', pkg.__path__)
mod0 = imp.load_module('mod1', tup[0], tup[1], tup[2])
print(issubclass(mod0.A, mod1.A)) # False


6 commentaires

Pourquoi n'utilisez-vous pas simple importer déclarations? Cela éviterait ce genre de problème.


Veuillez afficher l'instruction importer dans mod2 que les importations mod1 . Il importe si cette instruction est importer mod1 ou de pkg importer mod1 ou à partir de. Importer mod1 .


Vous avez importé le module contenant votre classe de base deux fois et tous les objets du module ont également été dupliqués. Essayez imprimer mod0.a est mod1.a et voir si ce n'est pas false . Ou même mod0 est mod1 .


@Sevenmarnach Je mettez en place un cadre de plug-in, où les noms des sous-classes ne sont disponibles que dans l'exécution et ne peuvent pas être codés en dur.


@liuyu: Il y a des cadres de plug-in Python. Si vous n'avez pas la connaissance intime du mécanisme d'importation Python, je suggère d'utiliser l'un de ces cadres plutôt que de rouler votre propre. Quoi qu'il en soit, le fait que les "noms des sous-classes ne soient disponibles que dans l'exécution" ne devraient pas vous empêcher de coder une déclaration d'importation, non?


@kindall tous les deux (mod0.a est mod1.a) et (mod0 est MOD1) dans Test_2 sont faux. Deux Instances du même module importées à partir de différents chemins sont considérés comme différents modules (au moins vrai pour Python 2.7 et Python 3.2). Sinon, il n'y aurait pas du tout le problème.


3 Réponses :


9
votes

Ils ne sont pas em> la même classe. Ils ont été créés avec le même code, mais comme vous avez exécuté ce code deux fois (une fois dans l'importation et une fois dans load_module), vous obtenez deux objets de classe différents. issubclass code> comparaisse l'identité des objets de classe et ils sont différents.

Edit: puisque vous ne pouvez pas compter sur issubclass code>, une alternative possible consiste à créer un Attribut unique sur la classe de base qui sera hérité par les classes dérivées. Cet attribut existe également sur des copies de la classe. Vous pouvez ensuite tester pour l'attribut. P>

class A:
    isA = True

class B(A):
    pass

class C:
    pass

def isA(aclass):
    try:
        return aclass.isA
    except AttributeError:
        return False

print isA(A)
True
print isA(B)
True
print isA(C)
False


6 commentaires

"Doc, ça fait mal quand je fais ça!" "Ne fais pas ça."


@liuyu, je n'ai pas été en mesure de tester cela, mais vous pourrez peut-être vérifier si les classes sont égales par correspondance: issubclass (mod0.a, mod1.a) ou mod0.a == mod1.a


@MarkRansom (mod0.a == mod1.a) est évalué à FALSE. Et vous avez raison, ce sont des classes différentes résultant du même code exécuté deux fois, c'est-à-dire ID (MOD0.A) et ID (MOD1.A) diffèrent.


@liuyu, a ajouté une solution de contournement pour vous. Désolé je ne pouvais pas y arriver plus tôt.


J'ai un cas similaire. Dans File-A, je définis une classe de base, qu'une classe dans les sous-classes de fichier B. Dans File, a, j'utilise ensuite impossible_source pour charger la classe dans File-b. Votre réponse s'applique-t-elle, plus particulièrement "Ils ne sont pas la même classe" ?


@Jeppe C'est ce que j'ai trouvé à travers un débogage. Si j'avais pris la classe partagée et que je l'ai déplacée dans un sous-répertoire des classes qui en avait besoin, je n'avais pas à utiliser load_module ou une variante, ce qui signifiait qu'il n'a été chargé qu'une fois sur le principal / sous Cours de répertoire. A définitivement appris une leçon intéressante ici.



1
votes
#!/usr/bin/env python

import os
import sys
import pkg
from pkg import mod1
import imp


def smart_load_module(name, path):
    # get full module path
    full_path = os.path.abspath(os.path.join(path[0], name))

    for module_name, module in sys.modules.items():
        # skip empty modules and ones without actual file
        if not module or not hasattr(module, '__file__'):
            continue

        # remove extension and normalize path
        module_path = os.path.abspath(os.path.splitext(module.__file__)[0])
        if full_path == module_path:
            return module

    # if not found, load standard way
    tup = imp.find_module(name, path)
    return imp.load_module(name, tup[0], tup[1], tup[2])


if __name__ == '__main__':
    mod00 = smart_load_module('mod1', pkg.__path__)
    print(issubclass(mod00.A, mod1.A))  # True

    tup = imp.find_module('mod1', pkg.__path__)
    mod0 = imp.load_module('mod1', tup[0], tup[1], tup[2])
    print(issubclass(mod0.A, mod1.A))  # False
This works for me. I search class by full path in sys.modules and return loaded instance if any found.

0 commentaires

6
votes

Depuis que j'ai passé du temps avec ceci, je pensais partager ma solution:

import inspect

...

def inherits_from(child, parent_name):
    if inspect.isclass(child):
        if parent_name in [c.__name__ for c in inspect.getmro(child)[1:]]:
            return True
    return False

print inherits_from(possible_child_class, 'parent_class')
#True


1 commentaires

Juste curieux, pourquoi tranchez-vous du nom de première classe? Cela ne semble pas nécessaire.