Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
186 views
in Technique[技术] by (71.8m points)

Can I define how accessing python object (and not just its attributes) is handled?

I have a custom class in python, that I would like to behave in a certain way if the object itself (i.e., and not one if its methods/properties) is accessed.

This is a contrived minimal working example to show what I mean. I have a class that holds various pandas DataFrames so that they can separately be manipulated:

import pandas as pd
import numpy as np

class SplitDataFrame:
    
    def __init__(self, df0, df1):
        self._dfs = [df0, df1]
        
    def increase(self, num, inc):
        self._dfs[num] = self._dfs[num] + inc
        
    @property
    def asonedf(self):
        return pd.concat(self._dfs, axis=1)


d = SplitDataFrame(pd.DataFrame(np.random.rand(2,2), columns=['a','b']),
                   pd.DataFrame(np.random.rand(2,2), columns=['q','r']))
d.increase(0, 10)

This works, and I can examine that d._dfs now indeed is

[           a          b
 0  10.845681  10.561956
 1  10.036739  10.262282,
           q         r
 0  0.164336  0.412171
 1  0.440800  0.945003]

So far, so good.

Now, I would like to change/add to the class's definition so that, when not using the .increase method, it returns the concatenated dataframe. In other words, when accessing d, I would like it to return the same dataframe as when typing d.asonedf, i.e.,

           a          b         q         r
0  10.143904  10.154455  0.776952  0.247526
1  10.039038  10.619113  0.443737  0.040389

That way, the object more closely follows the pandas.DataFrame api:

  • instead of needing to use d.asonedf['a'], I could access d['a'];
  • instead of needing to use d.asonedf + 12, I could do d + 12;
  • etc.

Is that possible?

I could make SplitDataFrame inherit from pandas.DataFrame, but that does not magically add the desired behaviour.

Many thanks!

question from:https://stackoverflow.com/questions/65670961/can-i-define-how-accessing-python-object-and-not-just-its-attributes-is-handle

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You could of course proxy all relevant magic methods to a concatenated dataframe on demand. If you don't want to repeat yourself endlessly, you could dynamically do that.

I'm not saying this is the way to go, but it kind of works:

import textwrap

import pandas as pd
import numpy as np


class SplitDataFrame:
    def __init__(self, df0, df1):
        self._dfs = [df0, df1]

    def increase(self, num, inc):
        self._dfs[num] = self._dfs[num] + inc

    for name in dir(pd.DataFrame):
        if name in (
            "__init__",
            "__new__",
            "__getattribute__",
            "__getattr__",
            "__setattr__",
        ) or not callable(getattr(pd.DataFrame, name)):
            continue
        exec(
            textwrap.dedent(
                f"""
                def {name}(self, *args, **kwargs):
                    return pd.concat(self._dfs, axis=1).{name}(*args, **kwargs)
                """
            )
        )

As you might guess, there's all kind of strings attached to this solution, and it uses horrible practices (using exec, using dir, ...).

At the very least I would implement __repr__ so you don't lie to yourself about what kind of object this is, and maybe you'd want to explicitly enumerate all methods you want defined instead of getting them via dir(). Instead of exec() you can define the function normally and then set it on the class with setattr.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...