Adding class attributes using a for loop in Python
Asked Answered
C

6

18

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?

Cart answered 15/2, 2010 at 10:58 Comment(0)
J
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
Jaine answered 15/2, 2010 at 11:14 Comment(4)
That must have been what I remember seeing. ThanksCart
In fact, I think it is nicer than using setattr - so I wouldn't say its inelegant at allCart
You are not supposed to change the locals() dict: docs.python.org/library/functions.html#localsLieberman
I forgot about that :-(. What do you reckon the risk of breakage is?Cart
E
9

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()
Eryneryngo answered 15/2, 2010 at 11:35 Comment(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.Engine
B
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.

Boyles answered 15/2, 2010 at 11:2 Comment(0)
L
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()
Lapointe answered 15/2, 2010 at 11:19 Comment(0)
S
3
class Attr:
  def __init__(self):
    for key in objects_type:
      setattr(Attr, key, lambda cl: list())
Selie answered 15/2, 2010 at 11:5 Comment(2)
+1: Good. Why not a "new style" class that inherits from object?Ctenidium
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.Lapointe
I
-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.

Ita answered 11/7, 2020 at 22:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.