Does Python have class prototypes (or forward declarations)?
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>
Related videos on Youtube
Mat
Updated on February 06, 2020Comments
-
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 about 15 yearsFWIW, it's called en.wikipedia.org/wiki/Forward_declaration, not prototype (en.wikipedia.org/wiki/Prototype-based_programming).
-
Mat about 15 yearsIt's called function prototype in Kernighan and Ritchie, where I remember it from.
-
Alex S about 15 yearsJust checked, no "class prototypes" in my K&R copy ;)
-
Shayne over 11 yearsYeah its a bit confusing because the concept of prototypes in OOP and Functional coding are completely unrelated. o_O
-
Dilum Ranatunga over 6 yearsThis 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 over 15 yearsI'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 over 15 yearsYes, since the c=C() is in the class definition (executed upon module load), class C does not yet exist.
-
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 over 15 yearsclass 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 over 15 yearsThis 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 over 15 yearsInteresting 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 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 over 15 yearsWell, 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 over 15 yearsGood 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 over 15 yearsTrying 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 over 15 yearsVery 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 over 15 yearsRight, 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 about 14 yearsIn 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 about 14 yearsself.c will add an instance property, not a class level property.
-
verveguy about 14 yearsI 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 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 about 14 yearsIt 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 about 14 yearsJoe: 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 over 12 yearsThat 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 almost 8 yearsDjango specific question: stackoverflow.com/questions/7298326/…
-
Sérgio about 7 yearsthe 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 .