Reputation: 12177
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?
Upvotes: 3
Views: 809
Reputation: 7083
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
Upvotes: 1
Reputation: 20
Well, there is a a kind of messy way to do this using the "exec()" function which takes any string and executes it; then you can build your classes as a string and run that. Here is an example which builds your classes, you can see that they are declared by looking in the global namespace which is printed at the end.
classList = ['foo', 'bar', 'baz']
class BaseCalc():
def __init__ (self, dataFrame):
self.column = dataFrame[className]
def calc ():
return self.column.sum()
for className in classList:
template = '''class %s_calc(BaseCalc):
def __init__ (self, dataFrame):
self.column = dataFrame[className]
def calc ():
return self.column.sum()''' % className
exec(template)
print(globals())
Upvotes: 0
Reputation: 1
You can use the type function, however, to create such dynamic classes. names = ['name1', 'name2'] Suppose you have to create 10 class objects in python, and do something with them, like: obj_1 = MyClass() other_object.
Hope I could help you.
Upvotes: 0