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
143 views
in Technique[技术] by (71.8m points)

Can you make classes in a loop in python?

I have a situation where I'm making a bunch of classes, and a bunch of them are really basically the same, so I'd like to make them in a loop. They are being used with a registration system, so there's no problem one the USAGE side, but I'm not sure how to actually define a class with a variable determining the class name...

Simple example:

classList = ['foo', 'bar', 'baz']

for className in classList:
    class {{{className}}}_calc(BaseCalc): 
         def __init__ (self, dataFrame):
             self.column = dataFrame[className]
         def calc ():
             return self.column.sum()

This is a very simplified case, obviously. I can't change the arguments to init because there are a whole bunch of these already existing, that are part of a larger structure.

The rest of the example is using pandas syntax, just to give an idea of how this is being used... but it's actually being used with a SQL DB, and is much more complicated... I just don't want to have to defend "why are you doing this in the first place?" I have good reasons, leave it at that.

classname is in {{{ }}} in the class line to denote that it's a variable, and not actually syntactically correct there. The question is "how do I denote what I have used {{{ }}} for?" I think.

The answer may be metaclasses, but I'm still not sure how to make the NAME of my class variable....

ETA: Trying to use @python_user answer:

classList = ['foooo', 'bar', 'baaz']

class Base ():
    def __init__ (self, buq):
        self.buq = buq

    def getBuq(self):
        return 'buq: ' + self.buq
    

for cls in classList:
    class TEMP(Base):
        className = cls
        def __init__ (self):
            self.qux = len(cls)
            Base.__init__(self, cls)

        def blee(self, inpt):
            return inpt+ self.qux
    TEMP.__name__ = f'{cls}'
    TEMP.__qualname__ = f'{cls}'
    globals()[cls] = TEMP
f = foo()

f.getBuq()

>>>> 'buq: baaz'

This is only giving me the baaz class. All three are giving baaz... Am I doing something really dumb?

question from:https://stackoverflow.com/questions/65888722/can-you-make-classes-in-a-loop-in-python

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

1 Answer

0 votes
by (71.8m points)

You can do like so, using globals()

classList = ['foo', 'bar', 'baz']

for className in classList:
    class Temp:
        def __init__ (self, dataFrame):
            self.column = dataFrame[className]
        def calc ():
            return self.column.sum()
    Temp.__name__ = className
    globals()[className] = Temp

You can then do foo() to create an object of class foo. You have to call with the args required for the __init__ method for the actual example. This is just to show it works.

print(type(foo()).__name__) # foo
print(type(bar()).__name__) # bar
print(type(baz()).__name__) # baz

If you want to change your class names then you can do

Temp.__name__ = f'{className}_calc'
globals()[f'{className}_calc'] = Temp

As pointed out by wim in the comments you need to also set __qualname__. Temp.__qualname__ = f'{className}_calc'

Edit:

As name lookups in methods of a class are performed at runtime (not at compile time), the code snippet has a bug. className would always refer to the last element in the classList (baz in this case), this name exists outside the scope of the loop and will be the value for className in the methods for ALL classes. (eg : self.column = dataFrame[className]). <--- This is always dataFrame['baz']

To fix this, one has to declare a class level variable called className and assign className (the one from the loop) to that. So at compile time this value will be bound to the class. All reference to className inside the methods of the class needs to be changed to self.className for the code to work as expected.

class Temp:
    className = className # note this line
    def __init__ (self, dataFrame):
        self.column = dataFrame[self.className] # and this line with the original snippet

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

...