web-dev-qa-db-fra.com

Python a-t-il des prototypes de classe (ou des déclarations en aval)?

J'ai une série de classes Python dans un fichier. Certaines classes font référence à d'autres.

Mon code ressemble à ceci:

class A():
    pass

class B():
    c = C()

class C():
    pass

En essayant de l'exécuter, j'obtiens NameError: name 'C' is not defined. C'est assez correct, mais y a-t-il un moyen de faire en sorte que cela fonctionne ou dois-je réorganiser manuellement mes cours pour y répondre? En C++, je peux créer un prototype de classe. Est-ce que Python a un équivalent?

(Je joue en fait avec les modèles Django, mais j'ai essayé de ne pas compliquer les choses).

32
Mat

En Python, vous ne créez pas de prototype en tant que tel, mais vous devez comprendre la différence entre "attributs de classe" et attributs de niveau instance. Dans l'exemple que vous avez montré ci-dessus, vous déclarez un attribut de classe sur la classe B, et non un attribut de niveau instance.

C'est ce que vous recherchez:

class B():
    def __init__(self):
        self.c = C()
34
Joe Holloway

En réalité, tout ce qui précède constitue une excellente observation à propos de Python, mais aucune d’entre elles ne résoudra votre problème.

Django a besoin d'introspection.

La manière right de faire ce que vous voulez est la suivante:

class Car(models.Model):
    manufacturer = models.ForeignKey('Manufacturer')
    # ...

class Manufacturer(models.Model):
    # ...

Notez l'utilisation du nom de classe en tant que string plutôt que la référence de classe littérale. Django offre cette alternative pour traiter exactement le problème que Python ne fournit pas de déclaration en aval.

Cette question me rappelle la question du support technique classique que vous devriez toujours demander à tout client ayant un problème: "Qu'est-ce que vous vraiment essayez de faire?"

48
verveguy

Cela résoudrait votre problème tel que présenté (mais je pense que vous recherchez réellement un attribut d'instance auquel jholloway7 a répondu):

class A:
    pass

class B:
    pass

class C:
    pass

B.c = C()
8
truppo

Python n'a pas de prototypes ni de classes ouvertes de style Ruby. Mais si vous en avez vraiment besoin, vous pouvez écrire une métaclasse qui surcharge new afin qu’elle effectue une recherche dans l’espace de nom actuel pour voir si la classe existe déjà et si elle retourne l’objet type existant au lieu de créer un objet. nouveau. J'ai fait quelque chose comme ça sur un ORM que j'ai écrit il y a un moment et cela a très bien fonctionné.

4
Erik Engbrecht

Toutes les réponses correctes concernant les attributs de classe et d'instance. Cependant, la raison pour laquelle vous avez une erreur est simplement l’ordre dans lequel vous définissez vos classes. Bien sûr, la classe C n’a pas encore été définie (car le code de niveau de classe est exécuté immédiatement à l’importation):

class A():
    pass

class C():
    pass

class B():
    c = C()

Marchera.

0
Ali Afshar

Une décennie après que la question a été posée, j'ai rencontré le même problème. Bien que certaines personnes suggèrent que le référencement soit effectué dans la méthode init , il est parfois nécessaire d’accéder aux données en tant qu '"attribut de classe" avant que la classe ne soit réellement instanciée. Pour cette raison, j'ai proposé une solution simple utilisant un descripteur.

class A():
    pass

class B():
    class D(object):
        def __init__(self):
            self.c = None
        def __get__(self, instance, owner):
            if not self.c:
                self.c = C()
            return self.c
    c = D()

class C():
    pass

>>> B.c
>>> <__main__.C object at 0x10cc385f8>
0
Louis Ng