Does Python have class prototypes (or forward declarations)?

44,641

Solution 1

In Python you don't create a prototype per se, but you do need to understand the difference between "class attributes" and instance-level attributes. In the example you've shown above, you are declaring a class attribute on class B, not an instance-level attribute.

This is what you are looking for:

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

Solution 2

Actually, all of the above are great observations about Python, but none of them will solve your problem.

Django needs to introspect stuff.

The right way to do what you want is the following:

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

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

Note the use of the class name as a string rather than the literal class reference. Django offers this alternative to deal with exactly the problem that Python doesn't provide forward declarations.

This question reminds me of the classic support question that you should always ask any customer with an issue: "What are you really trying to do?"

Solution 3

This would solve your problem as presented (but I think you are really looking for an instance attribute as jholloway7 responded):

class A:
    pass

class B:
    pass

class C:
    pass

B.c = C()

Solution 4

Python doesn't have prototypes or Ruby-style open classes. But if you really need them, you can write a metaclass that overloads new so that it does a lookup in the current namespace to see if the class already exists, and if it does returns the existing type object rather than creating a new one. I did something like this on a ORM I write a while back and it's worked very well.

Solution 5

A decade after the question is asked, I have encountered the same problem. While people suggest that the referencing should be done inside the init method, there are times when you need to access the data as a "class attribute" before the class is actually instantiated. For that reason, I have come up with a simple solution using a descriptor.

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>
Share:
44,641

Related videos on Youtube

Mat
Author by

Mat

Updated on February 06, 2020

Comments

  • Mat
    Mat over 4 years

    I have a series of Python classes in a file. Some classes reference others.

    My code is something like this:

    class A():
        pass
    
    class B():
        c = C()
    
    class C():
        pass
    

    Trying to run that, I get NameError: name 'C' is not defined. Fair enough, but is there any way to make it work, or do I have to manually re-order my classes to accommodate? In C++, I can create a class prototype. Does Python have an equivalent?

    (I'm actually playing with Django models, but I tried not complicate matters).

    • Alex S
      Alex S about 15 years
    • Mat
      Mat about 15 years
      It's called function prototype in Kernighan and Ritchie, where I remember it from.
    • Alex S
      Alex S about 15 years
      Just checked, no "class prototypes" in my K&R copy ;)
    • Shayne
      Shayne over 11 years
      Yeah its a bit confusing because the concept of prototypes in OOP and Functional coding are completely unrelated. o_O
    • Dilum Ranatunga
      Dilum Ranatunga over 6 years
      This issue is cropping up more with type tags (the return types of methods need to be declared in advance). Looks like the path of least resistance is to define types bottom up, even though such an arrangement is not the most readable layout.
  • Dana
    Dana over 15 years
    I'd be curious for an explanation as to why Python can find the def'n of C when it's assigning an instance attribute but not a class level one. Is it because it's trying to do the assignment at class definition rather than at runtime?
  • mthurlin
    mthurlin over 15 years
    Yes, since the c=C() is in the class definition (executed upon module load), class C does not yet exist.
  • Joe Holloway
    Joe Holloway over 15 years
    @truppo is correct. When you declare class attributes the corresponding references are resolved when the module is loaded (i.e. the class is interpreted). The init method is analogous to constructors in other languages so references in its local scope don't have to resolve until invoked
  • user1066101
    user1066101 over 15 years
    class level variables are defined at class definition time -- you rarely have any use for class variables. Instance variables are defined when an instance is created. You almost always want these.
  • Dana
    Dana over 15 years
    This just goes to show how little I use class variables. I'd never run into this or even considered it was a what-if.
  • Mat
    Mat over 15 years
    Interesting answer, and I now have better search terms to understand class and instance-level attributes. I'm actually playing with Django models, so am not sure exactly how class versus instance-level attributes will affect that.
  • Joe Holloway
    Joe Holloway over 15 years
    @Mat Django uses class attributes to provide a declarative object-relational mapping API. They use meta-classes to map these declarations into instance-level attributes. I don't recommend starting with the models API to understand Python's OOP constructs. I would start with something more basic.
  • Ali Afshar
    Ali Afshar over 15 years
    Well, I see what you mean, but I think it would be more non-intuitive to try to use something that doesn't yet exist. Like doing: print a; a=5
  • Carl Meyer
    Carl Meyer over 15 years
    Good recommendations here. The only other thing I would add is that if you're actually running into this issue when trying to define a ForeignKey, you can simply pass the class name as a string and Django will resolve it.
  • Mat
    Mat over 15 years
    Trying to run some statements such as print a; a=5 clearly doesn't make much sense, but a class is self-contained and perfectly reasonable to forward reference if C-style class prototypes were available.
  • Mat
    Mat over 15 years
    Very true Carl. I've resolved it by ordering my classes to ensure they're declared prior to being referenced. But that is a very good point if reordering is insufficient (circular references, etc.)
  • Ali Afshar
    Ali Afshar over 15 years
    Right, well, that sounds like implicit hell. In python things are executed in a straight line, and being "class definition code" makes no difference. What you were trying to do is exactly the same as: print a; a=5
  • verveguy
    verveguy about 14 years
    In the case of Django models, this is the only answer that works since we're trying to modify the Class itself, not instances of the Class. Django introspects the classes and uses that metadata to drive it's ORM layer.
  • verveguy
    verveguy about 14 years
    self.c will add an instance property, not a class level property.
  • verveguy
    verveguy about 14 years
    I retract my comment - this is fine as a way to add class members to a Python class without needing forward decls. But it does not actually solve the problem for Django model declarations due to something (?) internal to the way Django processes these models.
  • Joe Holloway
    Joe Holloway about 14 years
    @verveguy And how does that deserve a -1 exactly? This post is over a year old, had 20 upvotes and now you think it's wrong? I think perhaps you didn't read his question, nor my response, nor the comments under the response. It's clear that my answer addressed his underlying misunderstanding of the OOP model in Python.
  • Joe Holloway
    Joe Holloway about 14 years
    It was clear from the question what the OP was trying to do, that it was Django-related and that the OP was trying to understand Python's model for object-oriented programming, because Django's ORM can be confusing to beginners.
  • verveguy
    verveguy about 14 years
    Joe: your answer was informative about Python's OOP model, true enough, but was not what the OP was looking for. He wanted to declare a class level property, and instead you helped him declare an instance property. Your statement "This is what you are looking for" is simply not true, hence my comment. Sorry that this bothers you.
  • ObscureRobot
    ObscureRobot over 12 years
    That may be true, but what the OP ended up asking was very different from the title of the question. I'm reluctant to change the title three years later, even though it is inaccurate.
  • Ciro Santilli OurBigBook.com
    Ciro Santilli OurBigBook.com almost 8 years
    Django specific question: stackoverflow.com/questions/7298326/…
  • Sérgio
    Sérgio about 7 years
    the answer is no , but is weird , could you exemplify your solution ? when a depends on b , b depends on c and c depends on a .