So I have a composition in which why my wrapper class contains a Pandas DataFrame. Because I want to add some behaviors to it, I've setup the following:
class DataFrameWrapper(dict):
def __init__(self, dataframe, *args, **kwargs):
self.df = dataframe
self.pandas_callables = [method_name for method_name in dir(self.df)
if callable(getattr(self.df, method_name))]
super(DataFrameWrapper, self).__init__()
def __getitem__(self, item):
return self.df[item]
def __getattr__(self, item):
if item in self.pandas_callables:
# this is a dataframe method call - forward to the dataframe & return that
return object.__getattribute__(self.df, item)
else:
try: # try to return our own attribute, if any
return object.__getattribute__(self, item)
except AttributeError as ex:
# likely a panda attribute. IF not, then it's a genuine attribute
# error, so we don't catch it and let it raise another exception
return object.__getattribute__(self.df, item)
Then I have say
class Foo(DataFrameWrapper):
def __init__(self, dataframe, *args, **kwargs):
super().__init__(dataframe, *args, **kwargs)
class Bar(Foo)
""" concrete implementation class """
class Baz(Foo)
""" concrete implementation class """
So that we can do:
bar = Bar(df)
bar.to_json()
bar.some_custom_method()
col = bar["column_name"]
Now, if I do say:
json = bar.to_json()
This works fine. However, I'd like to add additional processing in Foo, so I want to do:
class Foo(DataFrameWrapper):
def to_json(*args, **kwrags)
# do additional stuff
super().to_json(*args, **kwrags)
However in that case, __getattr__() is never called in the wrapper and I just get
AttributeError: 'super' object has no attribute 'to_json'
Why?
EDIT:
If I do something dumb, like this:
class DataFrameWrapper(dict):
### previous code unchanged ###
def __getattribute__(self, item):
if item == "to_json":
return object.__getattr__(self, item)
return object.__getattribute__(self, item)
Then the call to to_json() works. According to the docs, I would expect what hack I just did to be what should happen anyways.