-2

Below is a simple class I made. I would like to access the inner function like

obj = TestClass().TestClass(data)
obj.preprocess.gradient()

It is clear that that such a call would not work because preprocess is a function. How can I achieve what I want (I hope it is clear to you)

EDIT: This is a simplified case. I hope that other users who are not into machine learning find it easier to apply the proper function in the correct order (first the preprocessing, then e.g. clustering, afterwards plotting). I just removed the outer functions (preprocessing etc.) it works fine. Still I wonder if such an approach might be reasonable.

import numpy as np
from sklearn.preprocessing import StandardScaler

class TestClass:

    def __init__(self, data):
        self.data = data
        self._preprocessed = data

    # should not be a function but rather a "chapter" which
    # separates preprocessing from analysis method
    def preprocessing(self):

        def gradient(self):
            self._preprocessed = np.gradient(self._preprocessed, 2)[1]

        def normalize(self):
            self._preprocessed = StandardScaler().fit_transform(self._preprocessed)

    def cluster_analysis(self):

        def pca(self):
            pass
Moritz
  • 5,130
  • 10
  • 40
  • 81
  • 1
    I guess you mean `obj = TestClass(data)` ? Also, where does `preprocess` method come from, don't you mean `preprocessing` instead ? Moreover, I think that formulating a question would help us out. – keepAlive Nov 26 '17 at 15:29
  • Is this representative of the problem you're trying to solve, or just simplified? Because if this method exists solely so it can define other functions, there are better ways to achieve this. – Reti43 Nov 26 '17 at 15:42
  • 1
    `gradient` isn't an attribute of the return value of `preprocess`; it is just a local variable in the scope of `preprocess`. `preprocess` needs to return something that has a `gradient` attribute. Or, `preprocess` needs to be a simple instance attribute, not an instance method. – chepner Nov 26 '17 at 15:57
  • 2
    "It is clear that that such a call would not work because preprocess is a function". Very not true. Functions are first class objects in Python and often have attributes defined just like that (see the numpy funcs for a popular example). The reason that notation fails is because an inner function is not an attribute of the parent any more than any of the other transient objects in the parent namespace. – Mad Physicist Nov 26 '17 at 16:02
  • 1
    Not happy with my answer ? It does what you asked. Some precisions maybe ? – keepAlive Dec 10 '17 at 05:44

1 Answers1

0

A first approach (probably better than the second one that follows) would be to return a class instance possessing those two methods, thus favoring composition.

Otherwise, what about returning a typed dict, as follows

    #...
    @property
    def preprocessing(self):

        def gradient(self):
            self._preprocessed = np.gradient(self._preprocessed, 2)[1]

        def normalize(self):
            self._preprocessed = StandardScaler().fit_transform(self._preprocessed)

        callables_dict = {
            'gradient':gradient,
            'normalize':normalize,
        }

        return type('submethods', (object,), callables_dict)
    #...

Then you can call your submethod as (I guess) you want, doing

>>> TestClass(data).preprocessing.gradient
<unbound method submethods.gradient>

or

>>> TestClass(data).preprocessing.normalize
<unbound method submethods.normalize>

Depending on what you want to do, it may be a good idea to cache preprocessing so as to not redefine its inner functions each time it is called.


But as already said, this is probably not the best way to go.
keepAlive
  • 6,369
  • 5
  • 24
  • 39
  • Why awkwardly build a type inside a property method, when you could just use a `class` statement instead? Replacing `@property def preprocessing` with class preprocessing` and getting rid of the `callables_dict` and `return` statement and you have something equivalent, but better than what you have shown. No idea if it's really what the questioner wants though... – Blckknght Nov 27 '17 at 01:48
  • 1
    @Blckknght. Equivalent or better ? Actually I do so because [Most Python developers do not nest classes, so when you do so you break convention and increase maintenance cost.](https://stackoverflow.com/a/30376236/4194079). That being said I am neither 100% sure to understand what the OP wants :). – keepAlive Nov 27 '17 at 02:19
  • 1
    I think building a class with a `class` statement is almost always better than throwing methods in a dictionary and manually calling `type` to build the class at run time. Among the reasons for this is that your version will create a bunch separate classes. They'll have identical contents, but they won't be the same class, which might be very confusing if you try something like `isinstance(x.preprocessing(), x.preprocessing)`. But I think we probably agree that the Questioner's design is not very good, and that avoiding the inner class completely would be even better. – Blckknght Nov 27 '17 at 03:42