16

I was trying to generate a class from a dictionary:

class attr:
    for key in objects_type:
        setattr(attr, key, lambda cl: list())

This gives the error that attr is not defined during the for loop. I know I could write:

class attr:
    pass
for key in objects_type:
    setattr(attr, key, lambda cl: list())

But I am sure I remember seeing code similar to the first example somewhere. Does anyone know if it is possible to write something similar to the first form?

Casebash
  • 114,675
  • 90
  • 247
  • 350

6 Answers6

10

Although it's not very elegant, you can use locals():

>>> class c(object):
...     for i in range(10):
...         locals()['A' + str(i)] = i
... 
>>> c.A0
0
>>> c.A7
7
Max Shawabkeh
  • 37,799
  • 10
  • 82
  • 91
8

suppose you want to dynamically add class attributes such as these

classDict = {}
for i in range(2):
    classDict["func%s"%(i+1)] = lambda self:"X"

you can do this in several ways e.g. just add attributes after class has been created because you can'y easily access class name inside class

class Attr2(object):
    pass

for n,v in classDict.iteritems():
    setattr(Attr2, n, v)

print Attr2().func1(), Attr2().func2()

or better just create class on the fly e.g.

Attr3 = type("Attr3",(), classDict)
print Attr3().func1(), Attr3().func2()

or if you wish use metaclass e.g

class AttrMeta(type):
    def __new__(cls, name, bases, dct):
        dct.update(classDict)
        return type.__new__(cls, name, bases, dct)

class Attr4(object):
    __metaclass__ = AttrMeta

print Attr4().func1(), Attr4().func2()
Anurag Uniyal
  • 85,954
  • 40
  • 175
  • 219
  • 1
    I think the `type` method is the best solution, given the question. It does require the programmer to restructure the rest of how the class attributes are defined. On the other hand, it avoids digging into `locals()`, avoids defining a custom metaclass, and avoids mucking with the logic of any _other_ metaclasses in use. IMO, this makes it palatable for a library others will use and contribute to. – AlanSE Nov 01 '17 at 21:52
4

I don't exactly know, what you are doing. I can give you example of adding class attributes to a class in a for loop:

attributes = ('x', 5), ('y', 6), ('name', 'Cls')

class Cls:
  pass
for key, value in attributes:
  setattr(Cls, key, value)

Remember that for must be run after whole class is defined. You get error, that attr is not defined, because you want to access the class before you create it (class ... is a statement in python, which creates a class object). You must add attributes after you create a class and rememeber, those will be class attributes, not instance attributes.

gruszczy
  • 40,948
  • 31
  • 128
  • 181
4
newmeths = {
  'two': lambda self: 2,
}

class MC(type):
  def __init__(cls, name, bases, dict):
    for k, v in newmeths.iteritems():
      setattr(cls, k, v)
    super(MC, cls).__init__(name, bases, dict)

class C(object):
  __metaclass__ = MC
  pass

c=C()
print c.two()
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
3
class Attr:
  def __init__(self):
    for key in objects_type:
      setattr(Attr, key, lambda cl: list())
shuvalov
  • 4,713
  • 2
  • 20
  • 17
  • 1
    +1: Good. Why not a "new style" class that inherits from `object`? – S.Lott Feb 15 '10 at 11:11
  • 3
    This is the wrong place to do this. Not only is it wasteful, but existing bound methods will be different from bound methods from objects instantiated later. Well, more different than usual. – Ignacio Vazquez-Abrams Feb 15 '10 at 11:13
-2

I struggled with this for a long time and my eventual solution using an example (since I am so new to coding):

names = ['Cuba I', 'San Felipe II Okeechobee', 'Bahamas', 'Cuba II', 'CubaBrownsville', 'Tampico', 'Labor Day', 
'New England', 'Carol', 'Janet', 'Carla', 'Hattie', 'Beulah', 'Camille', 'Edith', 'Anita', 'David', 'Allen', 
'Gilbert', 'Hugo', 'Andrew', 'Mitch', 'Isabel', 'Ivan', 'Emily', 'Katrina', 'Rita', 'Wilma', 'Dean', 'Felix', 
'Matthew', 'Irma', 'Maria', 'Michael']

for i in range(len(names)):
    namev                                      =                    names[i]
    locals()[''+str(namev.replace(' ',''))+''] =                  Hurricane(
        name                                   =                      namev)

So 'Cuba I' would be 'CubaI' under the class 'Hurricane'. From my research from other Devs this is strongly not recommended, as to why I'm not sure yet. Seemed to work pretty well for the challenge at hand. Anyways do with it what you will.