tags:

views:

330

answers:

6

I have two classes that refer to each other, but obviously the compiler complains. Is there any way around this?

EDIT

Actually my code is slightly different than what Hank Gay uses. So python can definitely deal with some kinds of circular references, but it tosses an error in the following situation. Below is what I've got and I get an 'name Y not defined error'

class X(models.Model):

        creator = Registry()
        creator.register(Y)

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

Hope this helps clarify. Any suggestions.

+1  A: 

UPDATE: He changed the question after my answer. The currently accepted solution is better in light of the new question.

What are you saying is the problem?

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()

This compiles and runs just fine.

Hank Gay
A: 

http://amix.dk/blog/viewEntry/19343

spitfire
That's talking about circular module imports. This question says nothing about circular module imports--it's asking about circular class definitions.
Tim Lesher
oops! read it wrongly
spitfire
+2  A: 

As long as you are working within a method you can access the class object. Thus the example above has no problems if creator.register(Y) is moved inside init. However, you cannot have circular references to classes outside of methods.

Daniel
This answer doesn't explain anything. It gives a "workaround" without understanding or explaining the real issue, which is explained in the answers by Jonas and John Machin.
Van Gale
+5  A: 

In python, the code in a class is run when the class is loaded.

Now, what the hell does that mean? ;-)

Consider the following code:

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

When you load the module that contains the code, python will print hello. Whenever you create an x, python will print hello again.

You can think of def __init__(self): ... as equivalent with __init__ = lambda self: ..., except none of the python lambda restrictions apply. That is, def is an assignment, which might explain why code outside methods but not inside methods is run.

When your code says

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

You refer to Y when the module is loaded, before Y has a value. You can think of class X as an assignment (but I can't remember the syntax for creating anonymous classes off-hand; maybe it's an invocation of type?)

What you may want to do is this:

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

That is, create the class attributes of X which reference Y after Y is created. Or vice versa: create Y first, then X, then the attributes of Y which depend on X, if that's easier.

Hope this helps :)

Jonas Kölker
+1  A: 

The error is that execution of creator.register(Y) is attempted during the (executable) definition of class X, and at that stage, class Y is not defined. Understand this: class and def are statements that are executed (typically at import time); they are not "declarations".

Suggestion: tell us what you are trying to achieve -- perhaps as a new question.

John Machin
A: 

The problem is most likely not Python. I would think it is an SQL issue. The classes are via an abstraction layer converted to an SQL query to create a table. You are trying to reference from one table another one that at the time does not exist yet.

In SQL you would solve this by creating the table first without the references and after that modify them to make those references,

However I am not sure about my answer, so take it with lots of seasoning, I would be actually quite surprised if Django's database abstraction layer doesn't deal with cross references nicely.

Martin P. Hellwig