0

How can I create a dictionary class 'D' that stores instances of some class 'A' and inherits wrapped versions of the methods of 'A'?

For example, suppose a = A() and a.first() is a function that does something to 'a' and returns some object. Then I want d = D({'k0': a0, 'k1': a1}) to have an attribute d.first() that returns {'k0': a0.first(), 'k1': a1.first()} and changes the underlying data in a0 and a1 as would calls to first().

Additionally, I want to only expose methods (and possibly attributes) that do not already exist in the dict class (no stomping) AND I want to do this dynamically (no hard coding of the methods of A, I already know how to type all the methods out by hand). Maybe consider dealing only with methods that don't start with '_' if that is easier somehow.

Also, I would like 'D' to do some handling of outputs from the calls depending on the return types (post-processing, wrapping whatever).

It seems like this might be kind of a bad idea but I just want to understand what approaches there are. From my reading so far, it seems like I could use multiple inheritance and write my own __new__ function or do something else with metaclassing. But the lore is that you should generally not mess around with __new__ unless you are a guru so this is making me hesitate. Is there some decorator trick for doing this? I think I could also use the inspect module to traverse the class 'A' but this feels like hack.

Update: As a very specific example. Imagine I have a dictionary of pandas.DataFrames and that I want the collection to appear and behave as much as possible like a single DataFrame with even tab completion of methods working in iPython (and I'm not using a pandas.Panel). For simplicity, consider only read-only behaviour (indexing and selecting), things like d.ix[0:10] and d.mean() should return something from applying the function to all the frames in the dictionary whilst d['k0'] should return the value in the dictionary corresponding to key 'k0'. A combination of hard-coding and getattr does what I want, but I am exploring to see how this could be done with less hard-coding.

I think this is a common design problem where you want to dynamically generate an iterable that behaves somewhat like the objects it contains.

mathtick
  • 6,487
  • 13
  • 56
  • 101

2 Answers2

1

Each of these work in limited testing:

#! /usr/bin/python3
class D(dict):
    def __getattr__(self, name):
        return lambda *args, **kw: {
            k:getattr(v,name)(*args, **kw) for k,v in self.items() }

class D(dict):
    def __getattr__(self, name):
        def proxy(*args, **kw):
            d2 = {k:getattr(v,name)(*args, **kw) for k,v in self.items()}
            # Post-process if required
            return d2
        return proxy

d = D()

d['k1']='a1'
d['k2']='a2'
d['k3']='33'

print(d.capitalize())  # No-arg form passed to str, not dict
print(d.replace('a', 'Hello')) # Arguments
print(d.get('k1'))  # .get() satisified by dict, not str
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • __getattr__ seems to work for the functions but not for attributes such as d.ix[0:10] that basically have a __getitem__ method. I guess this could be checked for manually somehow. – mathtick Sep 04 '13 at 04:27
0

This is a bit of an odd question, but it very much depends on what 'D' is structurally. If D is just a data structure, then it is perfectly valid to pack the dictionary with the objects, subcript and call the functions like so:

class A(object):
   def __init__(self,x):
       self.x = x
   def first(self):
       return self.x

a = A(7)
b = A(3)
d = {'X':a,'Y':b}
print d['X'].first()

Alongside this, if you want all "firsts" then you can do something like this:

firsts = {}
for i,j in d:
    firsts[i]=j.first()

However, it seems like your challenge is a little more complex, plus unsafe: what if an objecy in the dictionary has a keys() method? Which takes precedence, and if its the dictionaries, how do I access the keys() method of the contents?

What I'd suggest is reframing, or reasking, the question with some more specifics. Firstly, what do the objects in the dictionary represent? And more importantly what does the dictionary represent?

  • The dict takes precedence. If you want to access the keys of the objects then you just do it d['k0'].keys() (no convenience function is given). The use case is basically iPython. Think of a collection of tables (or DataFrames) where you want to store them separately (mostly disjoint columns) but want to do operations on them as if they were one table. – mathtick Sep 03 '13 at 03:18