Quant Christo
Quant Christo

Reputation: 1430

Meta-programming - How to generate class from template when __class__ is used?

I've created class like this to solve issue with signature and feature_names:

import copy
from sklearn.feature_selection import VarianceTreshold


class VarianceThresholdN(VarianceThreshold):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.feature_names = None

    #hack from https://stackoverflow.com/questions/51430484/how-to-subclass-a-vectorizer-in-scikit-learn-without-repeating-all-parameters-in    
    def get_params(self, deep=True): 
        params = super().get_params(deep)
        cp = copy.copy(self)
        cp.__class__ = VarianceThreshold
        params.update(cp.__class__.get_params(cp, deep))
        return params

    def fit(self, X, y=None):
        self.feature_names = list(X.columns)
        return super().fit(X, y)

Unfortunately, I need to create a lot of classes like this, so a lot of copy-paste and replacing two things: class VarianceThresholdN(VarianceThreshold): to class DifferentClassN(DifferentClass): and cp.__class__ = VarianceThreshold to cp.__class__ = DifferentClass.

So there is a clear template, but due to cp.__class__ = ... I can't use mix-ins.

Probably this code could be generated with jinja templates, but is there any way to avoid it, with some pythonic trick from meta-programming?

Upvotes: 1

Views: 578

Answers (1)

Masklinn
Masklinn

Reputation: 42492

I really don't understand what that's supposed to do, you're pretty much just calling super() twice as far as I can tell, with the second version doing that explicitly.

Probably this code could be generated with jinja templates, but is there any way to avoid it, with some pythonic trick from meta-programming?

If your pattern is that you always want to "swap" self.__class__ for its parent, you can just access it using __bases__, or mro()[1]:

  • cls.__bases__ is a tuple of all the superclasses of cls, it's exactly what you put in the parens (excluding kwargs)
  • cls.mro() is the "method resolution order", it would work fine for this specific (completely linear) case: it's a sequence of all classes to go through when handling an attribute or method call, starting from cls (included) and ending at object (which is the original ancestor of all Python classes).

Upvotes: 1

Related Questions