38

I need to make a bunch of class variables and I would like to do it by looping through a list like that:

vars=('tx','ty','tz') #plus plenty more

class Foo():
    for v in vars:
        setattr(no_idea_what_should_go_here,v,0)

is it possible? I don't want to make them for an instance (using self in the __init__) but as class variables.

Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
pawel
  • 1,031
  • 2
  • 9
  • 16

7 Answers7

61

You can run the insertion code immediately after a class is created:

class Foo():
     ...

vars=('tx', 'ty', 'tz')  # plus plenty more
for v in vars:
    setattr(Foo, v, 0)

Also, you can dynamically store the variable while the class is being created:

class Bar:
    locals()['tx'] = 'texas'
Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
  • ah. that's cool. I will give it a go and see if the API I work with can handle that. thanks mate! – pawel Nov 29 '11 at 08:28
  • What if you've overridden __setattr__()? – JasonGabler Nov 30 '21 at 08:05
  • Update... I figured it out. Within ```__setattr__()``` you can call ```super(ThisClass, self).__setattr__(varname, varvalue)```. – JasonGabler Nov 30 '21 at 08:26
  • 1
    Regarding the last part of the answer,`locals()['tx'] = 'texas'`. It should be said that the Python documentation specifically warns against this: *Note: The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter.* Link: https://docs.python.org/3/library/functions.html#locals – Håkon T. Jan 12 '23 at 22:22
  • The locals for a class dictionary are modifiable. It is the locals for a function that will be ignored. – Raymond Hettinger Jan 12 '23 at 22:33
10

Late to the party but use the type class constructor!

Foo = type("Foo", (), {k: 0 for k in ("tx", "ty", "tz")})
costrouc
  • 3,045
  • 2
  • 19
  • 23
9

If for any reason you can't use Raymond's answer of setting them up after the class creation then perhaps you could use a metaclass:

class MetaFoo(type):
    def __new__(mcs, classname, bases, dictionary):
        for name in dictionary.get('_extra_vars', ()):
            dictionary[name] = 0
        return type.__new__(mcs, classname, bases, dictionary)

class Foo(): # For python 3.x use 'class Foo(metaclass=MetaFoo):'
    __metaclass__=MetaFoo # For Python 2.x only
    _extra_vars = 'tx ty tz'.split()
Duncan
  • 92,073
  • 11
  • 122
  • 156
  • This will not work as your solution has hard coded the values for `_extra_vars`. – MSS May 20 '21 at 07:37
  • @MSS you don't have to use `'tx ty tz'.split()`, you are allow to substitute your own expression which could reference a variable. – Duncan May 20 '21 at 10:24
1

The locals() version did not work for me in a class.

The following can be used to dynamically create the attributes of the class:

class namePerson:
    def __init__(self, value):
        exec("self.{} = '{}'".format("name", value)

me = namePerson(value='my name')
me.name # returns 'my name'
double-beep
  • 5,031
  • 17
  • 33
  • 41
  • This is an awfully wrong approach. You are setting an instance variable, not a class variable. Also why don't you simply use `setattr`? –  Mar 20 '22 at 09:29
1

setattr(object, name, value) This is the counterpart of getattr(). The arguments are an object, a string and an arbitrary value. The string may name an existing attribute or a new attribute. The function assigns the value to the attribute, provided the object allows it. For example, setattr(x, 'name', value) is equivalent to x.name = value.

afghani
  • 467
  • 5
  • 7
0

The function you need is:

setattr(obj, name, value)

This allows you to set named attributes for a given class (this can be self).

The built in documentation for this function is pretty self-explanatory:

Signature: setattr(obj, name, value, /)
Docstring:
Sets the named attribute on the given object to the specified value.

setattr(x, 'y', v) is equivalent to ``x.y = v''
Type:      builtin_function_or_method

Example use

One use of this is to use a dictionary to set multiple class attributes, in my case this was from xpath definitions. I felt this improved maintainability by keeping potentially more fragile xpath definitions all in one place:

class Person:
    def _extract_fields(self):
        ''' Process the page using XPath definitions '''
        logging.debug("_extract_fields(): {}".format(repr(self)))
        # describe how to extract data from the publicly available site
        # (kept together for maintainability)
        fields = {
            'staff_name':
            '//div[@id="staff_name"]//text()',
            'staff_dob':
            '(//div[@id="staff_dob"]//text())[1]'
        }
        # populate named class attributes from the dict
        for key in fields:
            setattr(self, key, self._parsed_content.xpath(fields[key]))

    def __init__(self):
        self._extract_fields()
moo
  • 1,597
  • 1
  • 14
  • 29
0

You can create global variables with "foo." (or the name of your class) at the beggining of the name:

vars=('tx','ty','tz') #plus plenty more

class Foo():
    pass

foo = Foo() # Instance the class

for i in vars:
    globals () ["foo." + i] = value