классы Python, которые ссылаются друг на друга - PullRequest
6 голосов
/ 26 июня 2009

У меня есть два класса, которые ссылаются друг на друга, но, очевидно, компилятор жалуется. Есть ли способ обойти это?

EDIT

На самом деле мой код немного отличается от того, что использует Хэнк Гей. Таким образом, python может определенно иметь дело с некоторыми видами циклических ссылок, но в следующей ситуации он выдает ошибку. Ниже приведено то, что у меня есть, и я получаю «ошибка« имя не определено »

class X(models.Model):

        creator = Registry()
        creator.register(Y)

class Y(models.Model):
    a = models.ForeignKey(X)
    b = models.CharField(max_length=200)

Надеюсь, это поможет уточнить. Любые предложения.

Ответы [ 6 ]

15 голосов
/ 26 июня 2009

В python код в классе запускается при загрузке класса.

Теперь, что, черт возьми, это значит? ; -)

Рассмотрим следующий код:

class x:
    print "hello"
    def __init__(self): print "hello again"

Когда вы загружаете модуль, содержащий код, python напечатает hello. Всякий раз, когда вы создаете x, python будет печатать hello again.

Вы можете думать о def __init__(self): ... как эквивалентном с __init__ = lambda self: ..., за исключением того, что ни одно из ограничений Python лямбда не применяется. То есть def - это присваивание, которое может объяснить, почему выполняется код вне методов, но не внутри методов.

Когда в вашем коде написано

class X(models.Model):
    creator = Registry()
    creator.register(Y)

Вы ссылаетесь на Y при загрузке модуля, прежде чем Y будет иметь значение. Вы можете думать о class X как о назначении (но я не могу вспомнить синтаксис для создания анонимных классов из рук в руки; возможно, это вызов type?)

Что вы можете сделать, это:

class X(models.Model):
    pass
class Y(models.Model):
    foo = something_that_uses_(X)
X.bar = something_which_uses(Y)

То есть создайте атрибуты класса X, которые ссылаются на Y после создания Y. Или наоборот: сначала создайте Y, затем X, а затем атрибуты Y, которые зависят от X, если это проще.

Надеюсь, это поможет:)

2 голосов
/ 26 июня 2009

Ошибка состоит в том, что во время (исполняемого) определения класса X предпринимается попытка выполнения creator.register(Y), и на этом этапе класс Y не определяется. Поймите это: class и def - операторы, которые выполняются (обычно во время импорта); они не "декларации".

Предложение: расскажите нам, чего вы пытаетесь достичь - возможно, в качестве нового вопроса.

2 голосов
/ 26 июня 2009

Пока вы работаете в методе, вы можете получить доступ к объекту класса.

Таким образом, в приведенном выше примере не возникает проблем, если creator.register(Y) перемещается внутрь __init__. Однако вы не можете иметь циклические ссылки на классы вне методов.

2 голосов
/ 26 июня 2009

ОБНОВЛЕНИЕ: Он изменил вопрос после моего ответа. В настоящее время принятое решение лучше в свете нового вопроса.

Что вы говорите, это проблема?

class A(object):
    def __init__(self):
        super(A, self).__init__()


    def b(self):
        return B()


class B(object):
    def __init__(self):
        super(B, self).__init__()


    def a(self):
        return A()

Это компилируется и работает просто отлично.

0 голосов
/ 26 января 2019

Как мы хорошо знаем, это выглядит как самоочевидное противоречие, которое мы пытаемся представить двумя независимыми, но взаимозависимыми сущностями только с момента их рождения в физическом мире. Но когда дело доходит до области программного обеспечения, мы часто сталкиваемся с проблемами такого рода, так называемыми «круговыми или взаимными ссылками». Это может быть более серьезно в объектно-ориентированном проектировании, в котором взаимодействующие программные элементы обычно определяются и связаны друг с другом в подражании таким образом, как физические, но все же в виде чисто логического существования.

Во многих языках программирования эти проблемы были решены путем объявления элементов, на которые следует ссылаться, во времени перед их элементами, на которые делается ссылка, просто в виде подписей (без определений тела) для функций или классов. Тем не менее, этот вид уклоняющихся уловок кажется более недоступным и бесполезным для основанного на сценариях языка, такого как Python.

В Python нам лучше подходить к циклической ссылке с точки зрения разработки программного обеспечения следующим образом:

  1. Гораздо лучше перепроектировать классы, не круглые, если это возможно; Есть несколько способов (например, де- или составление класса, функция обратного вызова, шаблоны наблюдателя или подписчика и т. д.), чтобы сделать ссылки между элементами внутри одного класса удаленными или обратными.

  2. В тех случаях, когда линеаризация некоторых круговых цепочек между элементами может вызвать более серьезную проблему в некотором аспекте, таком как качество или производительность, мы можем предпринять другую меру, чтобы разделить их традиционную фазу построения на две: создание и структурирование. Например, два человека в друзьях, которым суждено родиться абсолютно после наблюдения за рождением другого, могут сделать так, как они впервые родились, а затем завязать дружбу как раз перед тем, как произойдут какие-либо значимые и наблюдаемые события. Обратите внимание, что если мы сталкиваемся с какой-то чрезвычайной сложностью или нуждаемся в высокой степени целостности при работе с объединенными объектами, применение шаблона Фабрики окупится.

0 голосов
/ 26 июня 2009

Проблема, скорее всего, не в Python. Я думаю, что это проблема SQL. Классы через уровень абстракции преобразуются в запрос SQL для создания таблицы. Вы пытаетесь сослаться из одной таблицы на другую, которая в то время еще не существовала.

В SQL вы могли бы решить эту проблему, сначала создав таблицу без ссылок, а затем изменив их, сделав эти ссылки

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...