Discussion:
[Modeling-users] specifying objects' class
John Lenton
2004-02-09 21:57:06 UTC
Permalink
Hi all again.

We'll be using Modeling for a large(ish) database shortly, and we'd
like to have to make minimal changes to the generated files, to
minimize the work required upon a schema change, given that we'll
probably have a lot of them during the initial developement.

In other words, we'd like to be able to put the business logic in one
tree ('luca', for example), and have the Modeling stuff in another
tree ('mdl'), with any given class in luca inheriting from its par in
mdl. That's pretty easy to do, except that we're concerned that things
like

person.getAddresses()

(to borrow from Sample) would return objects from the 'mdl' tree, and
not our own.

Maybe we're just being too lazy, but we fear that, if we don't find a
way to do this at least semi-automagically, the worked involved will
be tedious, and thus error-prone.
--
John Lenton (***@vialibre.org.ar) -- Random fortune:
25 de Diciembre, Doom, Doom, Doom.
Sebastien Bigaret
2004-02-10 13:29:05 UTC
Permalink
Hi,

Your claim is understandable, and indeed, even when using cvs,
frequent re-generation of modules and thus, frequent merge to keep the
busineess logic in generated files can be a real pain.

That's the reason why the -B switch exists in the script
mdl_generate_python_code.py ;) Extracted for the --help:

<< 'base' scheme: adds a sub-module 'MDL' within the generated package. All
files within MDL/ are ALWAYS overwritten when the python code is
regenerated, while others (in the root package) are never overwritten
if they exist. This is probably the one you want to use if your model
changes often. >>

So with the 'base' generation scheme you'll be able to have the business
logic in a module with the generated stuff in an other one, separated,
just as you describe (except that you'll get the business logic in
package 'luca', and the mdl stuff in sub-package 'luca.MDL').

About the point on how classes are retrieved at run-time: this is
described here:
http://modeling.sourceforge.net/UserGuide/faq-change-class-location.html
This basically mean that the classes are searched from the information
enclosed in the model --and the model at runtime needs not be the
exact same one than the model that is used for generation (esp.
regarding the package, module and class' names).

A little remark: this is a little different than just inheriting from
the mdl-generated class: when inheritance comes in, e.g. for Employee
deriving from Person, you don't want to have:

Employee <|-- EmployeeBase
^
/_\
|
|
Person <|-- PersonBase.

rather you want:

Employee <|-- EmployeeBase <|-- Person <|-- PersonBase

so that Employee inherits from the business-logic defined in Person,
as expected.


Last, you might be interested in testing the dynamic creation of
classes. It is available through patch #814055 at:
https://sourceforge.net/tracker/index.php?func=detail&aid=814055&group_id=58935&atid=489337

and has been discussed there:
https://sourceforge.net/mailarchive/forum.php?thread_id=3151927&forum_id=10674
https://sourceforge.net/mailarchive/forum.php?thread_id=3233626&forum_id=10674
https://sourceforge.net/mailarchive/forum.php?thread_id=3239464&forum_id=10674

Shortly said, this means no code-generation, with the ability to just
define your business logic, with the mdl-specific stuff automatically
generated at runtime (you'll want to use the metaclass approach
then). Additionally, new-style classes makes things run quicker ;)
Note that while this is not integrated into the main trunk yet, this
will shortly be. In fact, it should already be there, if only I have
had the time for it...

-- Sébastien.
Post by John Lenton
Hi all again.
We'll be using Modeling for a large(ish) database shortly, and we'd
like to have to make minimal changes to the generated files, to
minimize the work required upon a schema change, given that we'll
probably have a lot of them during the initial developement.
In other words, we'd like to be able to put the business logic in one
tree ('luca', for example), and have the Modeling stuff in another
tree ('mdl'), with any given class in luca inheriting from its par in
mdl. That's pretty easy to do, except that we're concerned that things
like
person.getAddresses()
(to borrow from Sample) would return objects from the 'mdl' tree, and
not our own.
Maybe we're just being too lazy, but we fear that, if we don't find a
way to do this at least semi-automagically, the worked involved will
be tedious, and thus error-prone.
--
25 de Diciembre, Doom, Doom, Doom.
John Lenton
2004-02-10 17:59:00 UTC
Permalink
Post by Sebastien Bigaret
That's the reason why the -B switch exists in the script
mdl_generate_python_code.py ;)
Oops, I didn realize that was what it was for :-/
Post by Sebastien Bigaret
A little remark: this is a little different than just inheriting from
the mdl-generated class: when inheritance comes in, e.g. for Employee
Employee <|-- EmployeeBase
^
/_\
|
|
Person <|-- PersonBase.
Employee <|-- EmployeeBase <|-- Person <|-- PersonBase
so that Employee inherits from the business-logic defined in Person,
as expected.
hmm... it isn't clear to me that this is always what you want;
currently we're using (in an unrelated field) a linearization of that
graph into

Employee <| Person <| EmployeeBase <| PersonBase

although I can see that in this case you are (on first sight at least)
correct. However, no matter what is expected, it is exactly what the
C3 MRO in python 2.3 does. If you want your behaviour, all you have to
do is declare Employee as

class Employee(EmployeeBase, Person)

whereas if you want the other behaviour you declare it as

class Employee(Person, EmployeeBase)

unfortunately this only applies to new style classes as of python 2.3;
I'm not certain the (hackish) MRO in 2.2 DWIMs.
Post by Sebastien Bigaret
Shortly said, this means no code-generation, with the ability to just
define your business logic, with the mdl-specific stuff automatically
generated at runtime (you'll want to use the metaclass approach
then). Additionally, new-style classes makes things run quicker ;)
Note that while this is not integrated into the main trunk yet, this
will shortly be. In fact, it should already be there, if only I have
had the time for it...
new-style classes would be nice for several reasons, not only
performance. For starters, the fact that you can query the MRO and use
super(), makes multiple inheritance manageable.

By the way, did you compare the speed of your metaclass approach to an
exec?
--
John Lenton (***@vialibre.org.ar) -- Random fortune:
The early worm gets the late bird.
Sebastien Bigaret
2004-02-10 18:48:10 UTC
Permalink
Post by John Lenton
Post by Sebastien Bigaret
[...]
Employee <|-- EmployeeBase <|-- Person <|-- PersonBase
so that Employee inherits from the business-logic defined in Person,
as expected.
hmm... it isn't clear to me that this is always what you want;
currently we're using (in an unrelated field) a linearization of that
graph into
Employee <| Person <| EmployeeBase <| PersonBase
although I can see that in this case you are (on first sight at least)
correct. However, no matter what is expected, it is exactly what the
C3 MRO in python 2.3 does. If you want your behaviour, all you have to
do is declare Employee as
class Employee(EmployeeBase, Person)
whereas if you want the other behaviour you declare it as
class Employee(Person, EmployeeBase)
unfortunately this only applies to new style classes as of python 2.3;
I'm not certain the (hackish) MRO in 2.2 DWIMs.
Fine, no problem! I only wanted to point out the fact that Employee
should inherit from both EmployeeBase and Person (and additionally, the
generated code works for py2.1, 2.2 and 2.3, so I used strict
single-inheritance).
Post by John Lenton
new-style classes would be nice for several reasons, not only
performance. For starters, the fact that you can query the MRO and use
super(), makes multiple inheritance manageable.
By the way, did you compare the speed of your metaclass approach to an
exec?
Sorry, I'm afraid I do not understand what "comparing to an exec"
means, could you be more explicit?

-- Sébastien.
John Lenton
2004-02-10 19:09:09 UTC
Permalink
Post by John Lenton
hmm... it isn't clear to me that this is always what you want;
currently we're using (in an unrelated field) a linearization of that
graph into
Employee <| Person <| EmployeeBase <| PersonBase
although I can see that in this case you are (on first sight at least)
correct. However, no matter what is expected, it is exactly what the
C3 MRO in python 2.3 does. If you want your behaviour, all you have to
do is declare Employee as
class Employee(EmployeeBase, Person)
whereas if you want the other behaviour you declare it as
class Employee(Person, EmployeeBase)
after discussing it with Federico (Heinz) I realized I wasn't being
particularly clear on this point, so, at risk of boring you all to
death over a subject you care little about, I'll expand on this a bit.

First, Federico understood that Sébastien is suggesting that the
EmployeeBase class must explicitely inherit from Person, which is
seems odd to me; could you (Sébastien) clarify that? What I understood
you to mean was that the EmployeeBase MRO was as you described. As one
would never instanciate *Base objects, all is well.

and now for something completely the same:

class PersonBase(object):
pass
class EmployeeBase(PersonBase):
pass
class Person(PersonBase):
pass
class Employee(EmployeeBase, Person):
pass
print ' Person MRO:', [i.__name__ for i in Person.mro() ]
print ' Employee MRO:', [i.__name__ for i in Employee.mro() ]
class Employee(Person, EmployeeBase):
pass
print 'Alt. Employee MRO:', [i.__name__ for i in Employee.mro() ]

just to be clear, IMHO the first way is the right way to do it if you
know the subclasses will never want to override the superclass. This
is the case of concrete classes in a templated abstract factory, for
example. The second way is probably what you want most other times.
In both cases all bets are off if you ever instanciate a *Base.

Clearer?
--
John Lenton (***@vialibre.org.ar) -- Random fortune:
Is that really YOU that is reading this?
Sebastien Bigaret
2004-02-10 20:08:03 UTC
Permalink
Post by John Lenton
Post by John Lenton
hmm... it isn't clear to me that this is always what you want;
currently we're using (in an unrelated field) a linearization of that
graph into
Employee <| Person <| EmployeeBase <| PersonBase
although I can see that in this case you are (on first sight at least)
correct. However, no matter what is expected, it is exactly what the
C3 MRO in python 2.3 does. If you want your behaviour, all you have to
do is declare Employee as
class Employee(EmployeeBase, Person)
whereas if you want the other behaviour you declare it as
class Employee(Person, EmployeeBase)
after discussing it with Federico (Heinz) I realized I wasn't being
particularly clear on this point, so, at risk of boring you all to
death over a subject you care little about, I'll expand on this a bit.
First, Federico understood that Sébastien is suggesting that the
EmployeeBase class must explicitely inherit from Person, which is
seems odd to me; could you (Sébastien) clarify that? What I understood
you to mean was that the EmployeeBase MRO was as you described. As one
would never instanciate *Base objects, all is well.
I only meant to say that the single-inheritance "approach":

Emp -> EmpBase -> Person -> PersonBase

is the one that is used when you generate python code with the mdl
script, so in a way Federico understood me right ;) but I think I was
not clear myself in what I wrote.

The fact is that while py2.3 behaves as "expected", py2.2 does
not. You can refer e.g. to http://www.python.org/2.3/mro.html, section
Bad Method Resolution Orders. In fact, I did not want to << damage my
brain >> to make sure that the scripts behaves as expected for py2.1,
2.2 and 2.3, so I just made it simple. Hence, the single-inheritance
approach.

Then, to make it clear :)

- noone will probably ever want to instanciate *Base objects, right,

- so this is equivalent to stating that the Employee MRO is:
EmployeeBase, Person, PersonBase

- the generated code is less flexible than using py2.3
multiple-inheritance, but it ensures that the MRO is the same for
py2.1, 2.2 and 2.3 no matter how deep the inheritance hierarchy can
be (the deeper it is, the worse the brain damage can be when
thinking of MRO for all thos pythons :)
Post by John Lenton
pass
pass
pass
pass
print ' Person MRO:', [i.__name__ for i in Person.mro() ]
print ' Employee MRO:', [i.__name__ for i in Employee.mro() ]
pass
print 'Alt. Employee MRO:', [i.__name__ for i in Employee.mro() ]
just to be clear, IMHO the first way is the right way to do it if you
know the subclasses will never want to override the superclass. This
is the case of concrete classes in a templated abstract factory, for
example. The second way is probably what you want most other times.
In both cases all bets are off if you ever instanciate a *Base.
Clearer?
Absolutely! However, the code generated with the base scheme is minimal:
it defines entityName() plus the getters/setters for attributes and
relationships, and these methods are almost never overriden in
superclasses. So my opinion is finally that, *in the scope of the base
classes needed by the mdl framework*, this does not really make much
difference --tell me if I'm still missing an important point.

-- Sébastien.
Post by John Lenton
Is that really YOU that is reading this?
Is that really YOU asking this?-)

Loading...