[Tutor] Declaration order of classes... why it is important?

Dave Angel davea at ieee.org
Wed Aug 26 21:46:28 CEST 2009


Kent Johnson wrote:
> On Wed, Aug 26, 2009 at 12:25 PM, Mac Ryan<quasipedia at gmail.com> wrote:
>   
>> Hello everybody,
>>
>>        I am using "storm" (https://storm.canonical.com/) to manage my
>> database. In storm, relationships between tables (each table is
>> represented by a class) are expressed like this (line #4):
>>
>> 1 >>> class Employee(Person):
>> 2 ...     __storm_table__ =employee"
>> 3 ...     company_id =nt()
>> 4 ...     company =eference(company_id, Company.id)
>>
>> where Company is another class. Now, what I noticed is that Company must
>> be declared as a class before Employee, or python will throw an
>> exception (Company is not defined).
>>
>> I would be interested in understanding why this is so designed. I
>> expected that the exception would not be thrown at all, as I imagined
>> that the interpreter simply kept track of where classes were declared
>> and would try to evaluate the code only once an actual object would be
>> instantiated (at that point the interpreter would know where to look for
>> each class code).
>>     
>
> The body of a class definition is executed when it is encountered. The
> result is a class object (an instance of 'type', usually). Any names
> defined at class scope become attributes of the class. The class name
> becomes a reference to the class object.
>
>   
>> BTW, the behaviour I am describing is exactly what happens with function
>> declaration: the following code evaluates as expected, indeed.
>>
>> def fone():
>>  ftwo()
>> def ftwo():
>>  print "hello"
>> fone()
>>     
>
> Yes, functions are different than classes. The body of a function is
> not executed until it is called.
>
> Note that class methods behave like functions (well, they are
> functions) - they are not executed until called. But statements at
> class scope are executed to create the class.
>   
>> I would also be interested in knowing if there is a way around this or
>> if I simply have to live with it.
>>     
>
> You have to live with it unless you can put the attributes inside a
> method. In this case, I don't think that will work.
>
> Kent
>
>   
In Python, you don't declare classes, you define them.

You can forward reference inside a definition (or method), but not 
elsewhere.  That's because it's a one-pass system, where the lines are 
executed in order.  In the case of a "def", execution consists of 
compiling the body and making a function object.  That function object 
may forward reference all it likes, as long as it's not called until 
those references are available.

So there are two workarounds to get what you'd like.  Your problem is 
that you want the classes in a certain order, but that the first class 
has class attributes that have a forward reference.  You can't do that 
directly.

1)  As Kent suggested, you can put the attribute initialization inside a 
"dummy class method," one that will only be executed once, and that you 
promise will be executed before any other method of the class.  Then 
when you have finished defining the dependency (the other class), you 
call this dummy method.


Since you don't define all the other stuff, I have to simplify your 
case.  Give a working fragment, if this is too simplified.

class Employee(object):
   __storm_table__ = "employee"
   company_id = []
   company = Company.id         #where Company is a forward reference

class Company:
    id = 42


This gives an error  trying to define the Employee.company attribute.  
So define a classmethod to finish the job, and invoke it later

class Employee(object):
    @classmethod
    def finish(cls):
        cls.__storm_table__ = "employee"
        cls.company_id = []
        cls.company = Company.id         #where Company is a forward 
reference
        del cls.finish      #remove this method so it won't be called a 
second time

class Company:
    id = 42

Employee.finish()   #This finishes initializing the class


help(Employee)
print Employee.company_id

2) Simpler:   Simply define the forward referencing class method later 
in the file, in the same place you would have invoked finish().

class Employee(object):
        __storm_table__ = "employee"
        company_id = []
        #company = Company.id         #Do this later

class Company:
    id = 42

Employee.company_id = Company.id


DaveA


More information about the Tutor mailing list