I have methods that accept dicts or other objects and the names of "fields" to fetch from those objects. If the object is a dict then the method uses __getitem__
to retrieve the named key, or else it uses getattr
to retrieve the named attribute. This is pretty common in web templating languages. For example, in a Chameleon template you might have:
<p tal:content="foo.keyname">Stuff goes here</p>
If you pass in foo
as a dict like {'keyname':'bar'}
, then foo.keyname
fetches the 'keyname' key to get 'bar'. If foo
is an instance of a class like:
class Foo(object):
keyname = 'baz'
then foo.keyname
fetches the value from the keyname
attribute. Chameleon itself implements that function (in the chameleon.py26
module) like this:
def lookup_attr(obj, key):
try:
return getattr(obj, key)
except AttributeError as exc:
try:
get = obj.__getitem__
except AttributeError:
raise exc
try:
return get(key)
except KeyError:
raise exc
I've implemented it in my own package like:
try:
value = obj[attribute]
except (KeyError, TypeError):
value = getattr(obj, attribute)
The thing is, that's a pretty common pattern. I've seen that method or one awfully similar to it in a lot of modules. So why isn't something like it in the core of the language, or at least in one of the core modules? Failing that, is there a definitive way of how that could should be written?
__getitem__() is a magic method in Python, which when used in a class, allows its instances to use the [] (indexer) operators. Say x is an instance of this class, then x[i] is roughly equivalent to type(x). __getitem__(x, i) .
Python getattr() Function The getattr() function returns the value of the specified attribute from the specified object.
The Python getattr() function is used to obtain an object's attribute value and also provides the option of executing the default value if the attribute is not available.
What is getattr() used for? Explanation: getattr(obj,name) is used to get the attribute of an object. 6.
I sort of half-read your question, wrote the below, and then reread your question and realized I had answered a subtly different question. But I think the below actually still provides an answer after a sort. If you don't think so, pretend instead that you had asked this more general question, which I think includes yours as a sub-question:
"Why doesn't Python provide any built-in way to treat attributes and items as interchangable?"
I've given a fair bit of thought to this question, and I think the answer is very simple. When you create a container type, it's very important to distinguish between attributes and items. Any reasonably well-developed container type will have a number of attributes -- often though not always methods -- that enable it to manage its contents in graceful ways. So for example, a dict has items
, values
, keys
, iterkeys
and so on. These attributes are all accessed using .
notation. Items, on the other hand, are accessed using []
notation. So there can be no collisions.
What happens when you enable item access using .
notation? Suddenly you have overlapping namespaces. How do you handle collisions now? If you subclass a dict and give it this functionality, either you can't use keys like items
as a rule, or you have to create some kind of namespace hierarchy. The first option creates a rule that is onerous, hard to follow, and hard to enforce. The second option creates an annoying amount of complexity, without fully resolving the collision problem, since you still have to have an alternative interface to specify whether you want items
the item or items
the attribute.
Now, for certain kinds of very primitive types, this is acceptable. That's probably why there's namedtuple
in the standard library, for example. (But note that namedtuple
is subject to these very problems, which is probably why it was implemented as a factory function (prevents inheritance) and uses weird, private method names like _asdict
.)
It's also very, very, very easy to create a subclass of object
with no (public) attributes and use setattr
on it. It's even pretty easy to override __getitem__
, __setitem__
, and __delitem__
to invoke __getattribute__
, __setattr__
and __delattr__
, so that item access just becomes syntactic sugar for getattr()
, setattr()
, etc. (Though that's a bit more questionable since it creates somewhat unexpected behavior.)
But for any kind of well-developed container class that you want to be able to expand and inherit from, adding new, useful attributes, a __getattr__ + __getitem__
hybrid would be, frankly, an enormous PITA.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With