How to build Hierarchies of dict's? (Prototypes in Python?)
Toby
etatoby at gmail.com
Sun Feb 25 16:12:21 EST 2007
Charles D Hixson wrote:
> I want to access the values via instances of the class, so your D().a
> approach isn't suitable. More like: "t = D(); t[a]"
Well, D() is an instance of D, so D().a is the same as t = D(); t.a
In fact the various "a" attributes I'm accessing are class attributes of
the various classes, just as you asked.
If you'd rather use t[a], then things are simpler, because you won't
need to override __getattribute__, with all the ugliness that comes with
it: __getitem__ will suffice.
> Your approach uses techniques that I'm going to need to study
> carefully before I can hope to understand them.
Don't worry, here's a heavily commented version, just because it's
sunday and I have some free time ;-)
# inheriting from object so that we can use __getattribute__
# see http://www.python.org/download/releases/2.2.3/descrintro/
class AttrSearch(object):
'''Base class that customizes attribute access in all of its derived
classes, choosing between all the candidates in a custom way.'''
@classmethod
def getclassattrset(cls, name):
'''Class method (= polymorphic static method) that collects a set
of the attributes with a given name from itself and from all its
base classes (determined at runtime.)
The only argument is the name of the attribute to look up; the
return value is a list of (class_name, attribute_value) listing all
the candidates found.'''
# note: the first parameter 'cls' is the class of the runtime object
# on which this class method is called, much like 'self' is the
# object instance in instance methods
#
# notice: this method is defined on class AttrSearch, but if I call
# it on B (subclass of AttrSearch), then 'cls' is class B!
s = set() # a set is an unordered list without duplicates
try:
# __dict__ is the internal dictionary which holds all of a class
# or of an instance's attributes
# creating a tuple with the name of the runtime class 'cls'
# and the value of cls's attribute named name, and adding the
# tuple to the set (unless an equal tuple is already there)
s.add((cls.__name__, cls.__dict__[name]))
except KeyError:
# ...unless the class cls has no such attribute
pass
# for every base class, from which the runtime class inherits:
for base in cls.__bases__:
try:
# call this same method on them and add their results to the set
s.update(base.getclassattrset(name))
except AttributeError:
# except for the base classes which don't implement this method
# (for example the class object)
pass
return s # returning the collected set
def getattrset(self, name):
'''Instance method that collects a set of the attributes with a
given name from itself, from its class and from all the base classes.
The only argument is the name of the attribute to look up; the
return value is a list of (class_name, attribute_value) listing all
the candidates found. In case the attribute is also found in the
object itself, element 0 of the tuple is set to null.'''
# saving references to a couple of attributes we need to access
# directly, bypassing all this machinery; to achieve it, we
# explicitly call object's implementation of __getattribute__
self_dict = object.__getattribute__(self, '__dict__')
self_class = object.__getattribute__(self, '__class__')
s = set()
try:
# adding to the set the attribute named name in this very intance,
# if it exists, with None in place of the class name
s.add((None, self_dict[name]))
except KeyError:
# unless the instance doesn't have an attribute named name
pass
# addig to the set all the class attributes named name, from this
# object's class and all its base classes
s.update(self_class.getclassattrset(name))
return s
def __getattribute__(self, name):
'''Customized version of attribute fetching, that uses getattrset
(and thus getclassattrset) to get a list of all the attributes named
name before choosing which one to return.'''
# saving references to the attributes we need to access directly
self_class = object.__getattribute__(self, '__class__')
# calling AttrSearch's getattrset to do the dirty work
found = AttrSearch.getattrset(self, name)
# here is where you should examine 'found' and choose what to
# return; I only print what is available and return None
print 'Looking for "%s" in a %s instance, found %d candidates:' \
% (name, self_class.__name__, len(found))
print ' class value'
print ' ===== ====='
print '\n'.join([ " %-6s '%s'" % x for x in found ])
print '(now choose wisely what to return)'
return None
# example use of AttrSearch in a class hierarchy:
class A(AttrSearch):
a = 'attribute "a" of class A'
class B(A):
a = 'attribute "a" of class B'
class C(A):
a = 'attribute "a" of class C'
class D(B, C):
a = 'attribute "a" of class D'
t = D()
t.a = 'attribute "a" of instance t'
# --- end ---
Now if you ask for t.a, for example in a print statement, you get None,
but not before the following lines are printed to stdout:
Looking for "a" in a D instance, found 5 candidates:
class value
===== =====
D 'attribute "a" of class D'
B 'attribute "a" of class B'
A 'attribute "a" of class A'
None 'attribute "a" of instance t'
C 'attribute "a" of class C'
(now choose wisely what to return)
HTH
Toby
PS: I couldn't make out what you meant with your code... I fear it's
because of the hideous formatting :-)
More information about the Python-list
mailing list