1
votes

Hériter d'une classe donnée en argument

Ma configuration: je code en python 3.7, avec spyder4, sur une machine windows10.

Contexte

J'écris des classes pour un paquet. C'est une sorte de gestionnaire pour les classes définies dans un autre package que j'importe.

Voici une version simplifiée de la situation:

test name  
from class B  
from class B  

AttributeError: 'B' object has no attribute 'new_func'  

Les classes Letter_Class , A , B , C ont déjà été définis par quelqu'un d'autre (dans cet 'autre paquet' je je manipule) et je ne peux pas les changer. Je veux créer une classe, ici appelée "Dynamique", qui héritera d'une classe donnée en argument. Il doit toujours avoir les (nombreuses, nombreuses) méthodes que je souhaite ajouter telles que new_func () . Je ne peux pas savoir à l'avance quel enfant Letter_Class est utilisé.

Classes A , B , C code> ont à la fois des méthodes polymorphes et spécifiques, telles que source () et funcA () . Pour cette raison, Je ne peux pas faire en sorte que Dynamic hérite de ces classes . Cela aurait été trop facile.

Problème

Si j'exécute le code précédent et suivant:

# user executes in main: 
instance = Dynamic("test name", B)
print(instance.name)
instance.source()
instance.funcB()
instance.new_func()

J'obtiens:

# This I cannot modify, as they are in the package
class Letter_Class():
    def __init__(self, name):
        self.name = name

class A(Letter_Class):
    def source(self):
        print("from class A")
    def funcA(self):
        print("from class A")

class B(Letter_Class):
    def source(self):
        print("from class B")
    def funcB(self):
        print("from class B")

class C(Letter_Class):
    def source(self):
        print("from class C")
    def funcC(self):
        print("from class C")

# This I can modify
class Dynamic(Letter_Class):
    def __new__(self, name, this_class): # -------- issue lies here? --------
        obj = this_class(name)
        return obj

    def new_func(self):
        print("function defined in dynamic class")

L'instance est créée, source () et funcB () sont exécutés sans aucun problème. Ensuite, une erreur d'attribut est générée, car un objet Device n'a pas été vraiment créé.

Quelqu'un pourrait-il m'expliquer ce qu'il faut mettre dans la fonction constructeur __new __ () pour retourner réellement une instance de la classe Device , avec l'héritage approprié de this_class ? Je serais très reconnaissant.
Cette discussion semble résoudre le problème, mais dans un contexte différent et je ne peux pas appliquer la solution suggérée.


4 commentaires

"Je veux créer une classe, appelée ici" Dynamique ", qui héritera d'une classe donnée en argument." Cela n'a pas beaucoup de sens. L'héritage se produit lorsqu'une classe est définie pas lorsqu'une instance est créée. Il y a probablement une façon pirate de faire cela, mais vous ne devriez certainement pas. Probablement, vous voulez juste une sorte de classe proxy.


Alors, voici un question très similaire où j'ai donné une réponse montrant comment définir une classe proxy.


@ juanpa.arrivillaga Merci pour votre lien. J'avais trouvé une solution comme la vôtre, en redirigeant les appels vers une variable privée, de la classe que je souhaite encapsuler. Il me manquait la méthode spéciale __getattr __ () , et cela fait parfaitement l'affaire. Une classe proxy est exactement ce que je recherchais, je verrai si je peux l'implémenter dans mon projet. Merci beaucoup


Attention, un proxy comme celui-ci ne fonctionnera pas avec les méthodes dunder, par exemple __eq__ ou __str__ . C'est souvent très bien ... voici une solution de contournement que j'ai concoctée au cas où ce ne serait pas le cas, et d'autres réponses si cela vous pose problème ...


3 Réponses :


1
votes

Le problème est que vous définissez instance pour qu'elle égale la valeur de retour de la Letter_Class ; instance est un objet B et n'a aucune relation avec Dynamic .


1 commentaires

Oui, c'est exactement ce que j'ai vécu, et je ne savais pas comment faire autrement. Je n'ai pas expliqué cela, merci



1
votes

Vous pouvez utiliser une fonction d'usine qui construit vos instances de classe en utilisant la classe donnée pour hériter de:

from class A
from class A
from class B
from class B
from class C
from class C

sortie:

class Letter_Class:
    def __init__(self, name):
        self.name = name

class A(Letter_Class):
    def source(self):
        print("from class A")
    def funcA(self):
        print("from class A")

class B(Letter_Class):
    def source(self):
        print("from class B")
    def funcB(self):
        print("from class B")

class C(Letter_Class):
    def source(self):
        print("from class C")
    def funcC(self):
        print("from class C")


def make_dynamic_letterclass_factory(cls):
    """makes an returns a Letter_Class subclass instance that inherits
    from the class passed as argument
    """
    class Dynamic(cls):
        def __init__(self):
            pass
        def new_func(self):
            print("function defined in dynamic class")

    return Dynamic()


a = make_dynamic_letterclass_factory(A)
a.source(), a.funcA()

b = make_dynamic_letterclass_factory(B)
b.source(), b.funcB()

c = make_dynamic_letterclass_factory(C)
c.source(), c.funcC()

p >


1 commentaires

Cela semble faire l'affaire! Cependant, avec la façon dont je conçois ce package, j'irai plutôt avec une classe proxy proposée par @ juanpa.arrivillaga dans un commentaire plus élevé. Je mettrai bientôt à jour ma solution finale



0
votes

Solutions

J'ai trouvé une solution au problème de MON (grâce à la communauté SO). Pour tous les codeurs recherchant une solution à LEUR problème, le cas d'utilisation peut vous donner envie de choisir l'une de ces deux solutions:

fonctions d'usine

Utilisation une fonction d'usine qui construit votre classe - voir la réponse de Reblochon Masque à ce sujet.

en utilisant une classe proxy

Ceci a été suggéré par juanpa.arrivillaga dans cette question .

Voici la solution que je pense utiliser à partir de maintenant:

instance is called: 'foo bar'
from class B
from class B
calling a method defined in the Dynamic class

Je peux maintenant exécuter:

XXX

J'obtiendrai:

instance = Dynamic("foo bar", B)
print(f"instance is called: '{instance.name}'")
instance.source()
instance.funcB()
instance.new_func()

Tous les appels aux attributs qui ne sont PAS définis dans le La classe dynamique sera redirigée vers self.letter , avec la méthode magique: __getattr __ () . Utiliser __getattribute __ () à la place redirigerait TOUS les appels vers self.letter .

J'ai choisi cette approche spécifiquement parce que je s'attendent à ce que mes utilisateurs construisent leur propre type de classe Dynamique ; Je trouve plus simple de créer une classe proxy intermédiaire.

Cette ressource a également été utile, et a des indices pour créer une classe proxy beaucoup plus transparente : Proxy d'objets . Cependant, je n’ai pas essayé d’implémenter cela.

Alors, merci pour l’aide des gens, faites attention.
XC


0 commentaires