Есть ли в Python прототипы классов (или предварительные объявления)? - PullRequest
32 голосов
/ 08 февраля 2009

У меня есть серия классов Python в файле. Некоторые классы ссылаются на другие.

Мой код выглядит примерно так:

class A():
    pass

class B():
    c = C()

class C():
    pass

Пытаясь запустить это, я получаю NameError: name 'C' is not defined. Справедливо, но есть ли способ заставить это работать, или я должен вручную переупорядочить мои классы, чтобы приспособиться? В C ++ я могу создать прототип класса. Есть ли у Python эквивалент?

(на самом деле я играю с моделями Django, но старался не усложнять вопросы)

Ответы [ 6 ]

49 голосов
/ 25 февраля 2010

На самом деле, все вышеперечисленное является отличным наблюдением о Python, но ни одно из них не решит вашу проблему.

Джанго нуждается в самоанализе.

Способ вправо сделать то, что вы хотите, заключается в следующем:

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

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

Обратите внимание на использование имени класса в качестве строки , а не буквальной ссылки на класс. Django предлагает эту альтернативу для решения именно той проблемы, что Python не предоставляет предварительных объявлений.

Этот вопрос напоминает мне классический вопрос поддержки, который вы всегда должны задавать любому клиенту с вопросом: «Что вы действительно 1015 * пытаетесь сделать?»

34 голосов
/ 08 февраля 2009

В Python вы не создаете прототип как таковой, но вам нужно понимать разницу между «атрибутами класса» и атрибутами уровня экземпляра. В приведенном выше примере вы объявляете атрибут класса в классе B, а не атрибут уровня экземпляра.

Это то, что вы ищете:

class B():
    def __init__(self):
        self.c = C()
8 голосов
/ 08 февраля 2009

Это решит вашу проблему в представленном виде (но я думаю, что вы действительно ищете атрибут экземпляра, как ответил jholloway7):

class A:
    pass

class B:
    pass

class C:
    pass

B.c = C()
4 голосов
/ 08 февраля 2009

Python не имеет прототипов или открытых классов в стиле Ruby. Но если они вам действительно нужны, вы можете написать метакласс, который перегружает new , чтобы он выполнял поиск в текущем пространстве имен, чтобы увидеть, существует ли класс, и возвращает ли он существующий объект типа вместо создавая новый. Я сделал что-то подобное на ORM, я писал некоторое время назад, и это сработало очень хорошо.

0 голосов
/ 05 февраля 2019

Спустя десятилетие после того, как вопрос задан, я столкнулся с той же проблемой. В то время как люди предполагают, что ссылки должны выполняться внутри метода init , бывают случаи, когда вам необходимо получить доступ к данным как «атрибуту класса», прежде чем класс будет фактически создан. По этой причине я предложил простое решение с использованием дескриптора.

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 голосов
/ 08 февраля 2009

Все правильные ответы о атрибутах класса и экземпляра. Однако причина возникновения ошибки - просто порядок определения ваших классов. Конечно, класс C еще не определен (так как код уровня класса выполняется сразу при импорте):

class A():
    pass

class C():
    pass

class B():
    c = C()

Будет работать.

...