Feature Requests item #1110010, was opened at 2005-01-26 08:28 Message generated for change (Comment added) made by josiahcarlson You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=355470&aid=1110010&group_id=5470
Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: Python Library Group: None Status: Open Resolution: None Priority: 5 Submitted By: Gregory Smith (gregsmith) Assigned to: Nobody/Anonymous (nobody) Summary: 'attrmap' function, attrmap(x)['attname'] == x.attname Initial Comment: One of the side effects of the new-style classes is that objects don't necessarily have __dict__ attached to them. It used to be possible to write things like def __str__(self): return "Node %(name)s, %(nlinks)d links, active: %(active)s" % self.__dict__ ... but this doesn't work if the class doesn't have a __dict__. Even if does, I'm not sure it will always get members from base classes. There is a 'vars' function; you could put 'vars(self)' in the above instead of self.__dict__, but it still doesn't work if the class doesn't have a __dict__. I can see different solutions for this: (1) change the 'string %' operator so that it allows %(.name)s, leading to a getattr() on the right-side argument rather than a getitem. return "Node %(.name)s, %(.nlinks)d links, active: %(.active)s" % self (2) Make a builtin like vars, but which works when the object doesn't have a __dict__ I.e. attrmap(x) would return a mapping which is bound to x, and reading attrmap(x)['attname'] is the same as getattr(x,'attname'). Thus return "Node %(name)s, %(nlinks)d links, active: %(active)s" % attrmap(self) This attrmap() function can be implemented in pure python, of course. I originally thought (1) made a lot of sense, but (2) seems to work just as well and doesn't require changing much. Also, (1) allows cases like "%(name)s %(.name2)s", which are not very useful, but are very likely to be created by accident; whereas in (2) you are deciding on the right of the '%' whether you are naming attributes or providing mapping keys. I'm not sure it's a good idea change 'vars' to have this behaviour, since vars(x).keys() currently works in a predictable way when vars(x) works; whereas attrmap(x).keys() may not be complete, or possible, even when attrmap(x) is useful. I.e. when x has a __getattr__ defined. On the other hand, vars(x) doesn't currently do much at all, so maybe it's possible to enhance it like this without breaking anything. The motivation for this came from the "%(name)s" issue, but the attrmap() function would be useful in other places e.g. processdata( infile, outfile, **attrmap(options)) ... where options might be obtained from optparse, e.g. Or, an attrmap can be used with the new Templates: string.Template('Node $name').substitute( attrmap(node)) Both of these examples will work with vars(), but only when the object actually has __dict__. This is why I'm thinking it may make sense to enhance vars: some code may be broken by the change; but other code, broken by new-style classes, may be unbroken by this change. The proxy could be writable, so that attrmap(x)['a'] = y is the same as x.a = y .. which could have more uses. A possible useful (possibly weird) variation: attrmap accepts 1 or more parameters, and the resulting proxy is bound to all of them. when attrmap(x,y,z)['a'] is done, the proxy will try x.a, y.a, z.a until one of them doesn't raise AttributeError. So it's equivalent to merging dictionaries. This would be useful in the %(name)s or Template cases, where you want information from several objects. ---------------------------------------------------------------------- Comment By: Josiah Carlson (josiahcarlson) Date: 2005-10-26 23:16 Message: Logged In: YES user_id=341410 gregsmith: class attrmap: def __getitem__(self, key): return getattr(self, key) Use that as a mixin with any class you want to offer attribute mapping support to. As in... class foo(..., attrmap): ... Now your class has attribute mapping support. Amazing! As for the neatness of __dict__, I have used it before, but when I need to rely on using __dict__, I make sure my object has one (generally by not creating or using __slots__). ---------------------------------------------------------------------- Comment By: Gregory Smith (gregsmith) Date: 2005-10-26 11:14 Message: Logged In: YES user_id=292741 To josiahcarlson: Yes, I can add __getitem__ to any class. But I don't see why I should make a whole bunch of classes act like dictionaries, only so that I can pass them to single API (like string.Template) which requires a dictionary. A class should have a dictionary behaviour if it makes sense for that class; if I have to add it specifically, and only, to make it work well in one particular situation, that's a kluge. If I have to add dict behaviour to a whole bunch of classes to make them all work in that one situation, that's a serious kluge. And entirely unintuitive, to borrow a phrase. When you encounter this situation, IMO, it's clear that a little 'pipe-fitting' like this is the best solution. Furthermore, and to underline the above, what if I have a class which already has a __getitem__ interface for it's own purposes? Chances are this behaves in a way which makes it useless for harvesting general attributes from the object; the object always has a getattr interface, however. Since ALL objects have a getattr interface, and there's a fair number of APIs that require objects with a read-only, string-keyed dict interface, why not have this universal pipe-fitting to tie them together, so as not to have to modify the objects and/or interfaces. To rhettinger: First off, what do you mean by 'use case'? I gave three examples of the use of the function, with %attrmap(x), string.Template(...).substitute(attrmap(x)); attrmap(options). I can expand these to more detail if you like. I didn't give use examples for the 'write' case; I haven't encountered a use for that. Yes, you have to think a bit before you see what it's for. That's arguably true for super(), slice() and operator.attrgetter() as well. Once you've run into the situation where it's useful, it becomes pretty clear what it's for. Given the general problem it solves, it's hard to imagine a neater and more intuitive solution IMO. Here's a 2-line sales pitch, which was implied, but not given, in the original post: def __str__(self): return "MyObj(name=%(name)s, n=%(n)d, links = %(links)d )" % attrmap(self) vs. the harder-to-maintain return "MyObj( name=%s, n=%d, links = %d )" % (self.name, self.n, self.links) ...this requires the programmer to maintain the same list of symbols twice, a burden which is very common in languages inferior to Python. And obviously it's very simple (the base functionality is 5 lines). Like attrgetter, It becomes more useful when it's always available in the standard lib, and a little faster if it happens to be in C. to loewis: fair enough, maybe it doesn't qualify for a builtin. But it might make more sense to do it as operator.attrmap rather than UserDict.AttrMap; it seems to fit into the same category as attrgettr and itemgetter. I wasn't aware of those two when I originally posted. And am I the only one who thinks the first example I gave (with __str__ using "..."%self.__dict__) is a pretty neat trick? And who has noticed it's been broken with new-style classes? That by itself, IMO, calls for a fix, and replacing self.__dict__ with attrmap(self) is a pretty simple fix. ---------------------------------------------------------------------- Comment By: Raymond Hettinger (rhettinger) Date: 2005-06-26 21:18 Message: Logged In: YES user_id=80475 -1 While potentially useful, the function is entirely unintuitive (it has to be studied a bit before being able to see what it is for). Also, the OP is short on use cases (none were presented). IMO, this belongs as a cookbook recipe. ---------------------------------------------------------------------- Comment By: Josiah Carlson (josiahcarlson) Date: 2005-06-26 10:47 Message: Logged In: YES user_id=341410 An implementation of the base attrmap functionality (as suggested by Reinhold in his post in python-dev)... class attrmap: def __init__(self, obj): self.obj = obj def __getitem__(self, key): return getattr(self.obj, key) To Gregory Smith: If you merely add the __getitem__ method with a 'self' as the first argument of getattr, you don't even need attrmap. As an aside, when I want dictionary-like behavior from my classes, I use a dictionary or something that implements a subset of the mapping protocol. ---------------------------------------------------------------------- Comment By: Martin v. Löwis (loewis) Date: 2005-02-11 16:51 Message: Logged In: YES user_id=21627 I think I would rather not to see this as a builtin, e.g. putting it into UserDict.AttrMap instead. ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=355470&aid=1110010&group_id=5470 _______________________________________________ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com