Ian Bicking: the old part of his blog

So many accessors...

(Were you looking for Web-SIG?)

Both Cheetah and ZPT's TALES use a sort of soft sense of traversal. When you call \$person.address.city in Cheetah (or person/address/city in TALES) that might be equivalent to person().address().city(), or maybe person['address'].city or person().address()['city'] or any number of combinations.

This is really convenient. You don't have to think too much about what each object is. You are, after all, giving explicit names (address, city), and these names explain just what you are trying to do (different from if it meant person(address)[city]). What's really the difference between a dictionary and an attribute? A property and a method that takes zero arguments?

Part of the justification of this is that both Cheetah and ZPT/TALES are for displaying information. Your template isn't supposed to do anything, it's not supposed to have side-effects. So any methods you call shouldn't matter. And attributes and dictionaries have always been fuzzy -- every object has its __dict__ after all.

But even in spite of these objects, what really is the value of these different kinds of accessors? Does person.address really mean something different than person['address']? And what use is a method object really, do you often want to know what person.address is in addition to person.address()?

I think there are justifications for Python's various accessors, but I also think there's real value in simplifying these. We have some of that with property (and in one man's opinion, yay), and that allows us to avoid many methods (my general policy is that getting a property should never have side-effects, but otherwise zero-argument methods are fair game for propertyization). The attribute/dictionary distinction still exists, though.

I think my general rule will be: don't use __getitem__ unless you really want to create something dictionary-like. (Or maybe if you want to use attributes that aren't valid Python identifiers; then __getitem__ and __getattr__ maybe should be synonyms)

Created 22 Oct '03
Modified 25 Jan '05

Comments:

I'm in complete agreement with you about a "prohibition"
against attribute access causing side-effects, so long as
we're careful to define side-effects in terms of the
"true job" of the object, not "infrastructure" within the
object (which is to say caching, lazy evaluation, etc.,
would all implicitly induce side-effects in that they would,
respectively, update the cache, set the lazy pointer, etc.)

But to your real point :-) I like to think of the different
accessors as different namespaces. For a long time,
I've wanted to unify those namespaces, but I've finally
realized it's not always possible or desirable to do so.

In fact, in some recent design work I've been doing (no snarky
comments, here, Ian :-) ) on a more intuitive XML <-> Python
object mapping, I've been considering how to deal with the
plethora of namespaces XML has. I'm not even talking about
actual XML Namespaces, but, rather, the different
dimensions along which one can assign names in XML, like
tags/elements, attributes, entities, namespace prefixes, and
so forth.

In figuring out how to "intuitively" map that to Python, I'm
trying to wrap my head around the idea of using __getattr__
and __getitem__ and so forth to break things up and provide
separate namespaces mapping (inasmuch as it's possible) to
the appropriate XML counterparts.

The point of this long-winded comment being that it's quite
possibly instructive to think of .foo and ['foo'] as giving
access to namespaces within the object. Viewed from that
perspective, I'm even all for adding more namespaces and
syntactic sugar to support them :-) (of course, then we
risk wandering into Perl territory).

Imagine a Python that mapped these delimiters to "special"
object methods that you could use to provide various
namespaces of interest in your domain:

foo$bar
foo@bar
foo{'bar'}
foo!bar (not possible?)
foo?bar
foo
foo

foo[[bar]]
foo{{bar}}

I guess there are probably technical reasons to discard a bunch of these (e.g., [[bar]] creates a list within there, and {{bar}} a dict, right?)

But I hope you get my point :-) I mean, aside from destroying Python, which I assure you is not my aim. I'm just thinking aloud here, really.
# Tripp Lilley

I don't usually see people using both __getattr__ and __getitem__ namespaces very effectively -- though the critique holds more for getitem. For instance, on Web-SIG there are some proposals to have things like response['Content-Type'] = 'text/html'. (Not biting anyone's head off, these are just early proposals)

The idea is that we have a namespace of methods, but then we have this __getitem__ namespace on the response just sitting around doing nothing. The problem is backtracking -- seeing the response being used as a dictionary, is there any reason you'd assume it was referring to response headers? This is the case for most __getitem__ implementation, except in very clear mapping structures. It's fine to do response.headers['Content-Type'] = 'text/html', but it's important to contextualize that dictionary access with the headers attribute. At which point, why not allow response.headers.content_type = 'text/html'? Or maybe not...
# Ian Bicking

In JavaScript, the method/attribute and dictionary namespaces are the same; Python's separation of these namespaces allows you to create methods (in one namespace) that implement an object with a clean dictionary-like or array-like interface (in the other). Unifying them would mean that your methods would show up in your item namespace, and you'd just have to hope your users didn't happen to store something under the key 'lookup' or '_lookup' or whatever as an item.
# Kragen Sitaker