This is why posting this stuff pays off. Somebody comes back with a
cluestick in hand and helps out. :-)

On Thu, 2006-08-03 at 21:01 +0800, Russell Keith-Magee wrote:
> 
> On 8/3/06, Malcolm Tredinnick <[EMAIL PROTECTED]> wrote:
[...]
> 
>         (2) If a class has derived classes, you can access those via
>         attributes 
>         on the class. 
> 
> Obviously, I'd still prefer an _actual_ duck, but this seems a
> reasonable alternative in an duckless world. 

I could subclass everything from Duck if you like. Poor man's duck
typing. :-)

> I assume p.restaurant will return a fully functioning Restaurant
> instance? i.e., p.restaurant is really just a descriptor returning
> Restaurant.objects.get(pk=p.id) ? If so, +1

Yes, that's the idea. I can't see any reason not to do this, since the
database work is the same.

>                 (2) Not sure if we should go for
>                 Place.restaurant.italianrestaurant or just make 
>                 Place.italianrestaurant work directly (I prefer the
>         latter, but
>                 neither is a showstopper).
> 
> +1 to the latter syntax. When operating on an instance, the exact
> heirarchy is irrelevant. The only problem I can see here is in dealing
> with repeated names in the heirarchy, to which the only solution I can
> see is a model validation error.

I haven't worked out the API for specifying the attribute here, but
that's minor. Basically, we need something on the sub-class to say "use
this attribute name in the parent(s)". An attribute in the Meta class is
probably the solution, I guess. We already need to have an attribute for
specifying database column(s) back to the parent(s). Failure to set the
attribute and getting a name clash is a validation error.

[...]
> hrm... -0. I would have thought that a single isinstance() would be a
> better idea than a range of is_XXX() functions. e.g.,
> 
> p.isinstance(Restaurant) 
> 
> 
> (possibly using strings rather than/in addition to model modules if
> preferred or the implementation requries). This keeps the API small
> and simple, requires less 'magically appearing' functions which could
> potentially clash with user-defined functions, and keeps the API
> (reasonably) aligned with Python inheritance API. 

You might be right, I may be .. (wait! Let's not finish that thought.
Billy Joel has the copyright).

All your API points are well taken. I'm just being a coward on the
implementation side. It's harder that way, but that's not really a
reason to avoid doing the Right Thing(tm).

> This isn't necessarily an either/or suggestion - both _could_ be
> supported (but I would be -1 on having both). 

If an isinstance() isn't horrible, I would prefer that, for all the
reasons you mention. Agree that both is silly.

> This approach would also allow for checking multiple baseclasses
> simultaneously: 
> 
> p.isinstance(Restaurant, BaseballField)
> 
> In itself, this wouldn't be extraordinarily useful; if Restaurant and
> BaseballField both had a 'beer_price' attribute, you would still need
> to use p.restaurant.beer_price and p.baseballfield.beer_price... but
> more on this later 

Worth doing just to be in sync with how the isinstance() builtin works,
if nothing else. Your later points aren't invalid either.

>                 (4) Implementing a "what type am I" function is not
>         going to
>                 exist initially. 
> 
> Not sure about this. I agree that a generic form of this type of
> function is inherently expensive. However, the absence of ducks kind
> of makes this kind of function a requirement, IMHO.
> 
> To go back to the beer_price example, getting the value of the
> beer_price attribute gets a bit messy; it requires nested try/catches,
> and some extra effort to avoid code duplication: 

That is the reason for the is_XXX() (or single isinstance()) methods and
why I mentioned performance (and code readability) -- all those
try/excepts are going to be ugly and not free in Python cycles.

The question for me is whether this is really a use case or not. If
beer_price is common, why isn't it in a common base class? The only
reason I can think of is that it doesn't have the same type. Which gets
kind of interesting when you want to work with it later (although if it
has the same "interface" always, that is fine).

Kind of the reason I was punting on this was that the end-user (the
developer) would be more likely to understand their hierarchy and be
able to go to the things they cared about directly. Doing it in code
requires more general introspection and iteration over the all the
possibilities. On the other hand, your typical hierarchy is going to be
a couple of layers deep with a couple of classes, so it's hardly going
to kill you. 

> The multi-input isinstance might be one way to get around this. If
> p.isinstance returns the instance of the first class in the provided
> list that matches, or None if no class matches, the following would be
> possible: 
> 
> obj = p.isinstance(Restaurant, BaseballField)
> try:
>    price = obj.beer_price
>    print "Beer costs", price
> except:
>    print "No beer!"
> 
> isinstance() continues to work like a boolean interrogation, but it
> also provides the ability to act as a 'narrowing' method (to use the
> CORBA terminology). This provides a restricted 'what type am I'
> function; it will only check those types that are provided, rather
> than the full list of potential child types. 
> 
> I will concede that it isn't immediately obvious that isinstance()
> will return anything other than a boolean. I don't have any particular
> solution to this, other than providing isinstance() _and_ narrow() -
> but the duplicated functionality, sort-of-typed API grates against my
> sensibilities. 

Heh. I end up in "this feels like CORBA" land a lot, too, when I'm
thinking about this and trying to work out how to use it. :-)

I feel I can live without anything resembling narrow() -- or C++'s
dynamic_cast, for those more familiar with that language -- because I'm
not convinced there is an important use-case. However, if we leave it
out, it does makes accessing the beer_price attribute, wherever it may
live a little harder. It's also dangerous for me to be projecting my
usages onto the world wide group of developers. I have some experience,
but not *that* much. So, you probably have a point.

In passing, overriding __getattr__ isn't an ideal solution, either,
because it doesn't work well with lazy attribute resolving. I tried for
a while to make that work nicely, but never came away happy. (Although,
as I write this, it occurs to me that with introspection and caching
which models have which fields, we would only examine the "right"
tables. Hmmm.)

> I will also concede that implementing a narrow method of this sort is
> not a major drama, and could be easily written by the end user.
> However, it does strike me as a fairly essential component of a
> duckless inheritance system (as demonstrated by CORBA requiring a
> narrow method due to C++'s class system). My crystal ball forsees
> complaints if it is omitted.

My crystal ball says the complaints won't stop if it is, though. :-)

We're stuck between being explicit, efficient and being almost
transparently Python-like. I'm not sure all three goals can be met, so
we're discussing where to place the "here be bears" signs.

How about this? In the interests of getting the code finished and giving
people something to try out, we punt this one initially. It can be added
later, as you mention. Unless somebody has a really big brainwave very
soon, the main design here can't change too much, so debating whether we
have a narrow()-equivalent or not probably distracts from the big
picture. My thinking is along the lines of: if narrow() wasn't
available, would it be a showstopper for this design? If so, what is the
alternative?

I think the answer to the first question is "no", so I don't feel too
unhappy putting it in the "later" basket for now. 

Thanks heaps for all the feedback, Russ. Helps a lot.

Cheers,
Malcolm



--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-developers
-~----------~----~----~----~------~----~------~--~---

Reply via email to