18

I am trying to do something like this:

property = 'name'
value = Thing()
class A:
  setattr(A, property, value)
  other_thing = 'normal attribute'

  def __init__(self, etc)
    #etc..........

But I can't seem to find the reference to the class to get the setattr to work the same as just assigning a variable in the class definition. How can I do this?

slybloty
  • 6,346
  • 6
  • 49
  • 70
prismofeverything
  • 8,799
  • 8
  • 38
  • 53
  • 1
    That seems intentionally confusing. Why can't the attribute have a stable, easy-to-figure out name? Alternatively, why can't this attribute simply be a key in a dictionary? Why the magic? – S.Lott Mar 25 '10 at 22:36
  • I am using appengine, and their properties are defined as class variables. However, I am generating the admin automatically, so I have a list of the property names that will be used elsewhere, that I also want to use to create the properties so that each property only needs to be specified once. Basically I am trying to avoid adding an entry to the property list, and then adding a variable to the class, when really these have the same name and have the potential to get out of sync. – prismofeverything Mar 25 '10 at 22:51

4 Answers4

36

You can do it even simpler:

class A():
    vars()['key'] = 'value'

In contrast to the previous answer, this solution plays well with external metaclasses (for ex., Django models).

Vitalik Verhovodov
  • 634
  • 1
  • 6
  • 5
  • 1
    Another advantage is that it avoids the issues caused by the change in the syntax for specifying metaclasses between Python 2 and 3—so it's a more version independent approach. If you think about it, it effectively gives a `self` (or `cls`) argument to the code within a class definition. – martineau Jun 17 '18 at 21:08
  • This is excellent! I've often wanted to use setattr() on a class under construction, e.g., when doing table-driven construction of a bunch of similar properties. Before I either had to use a metaclass, or define a class method that I call right after the close of the class definition proper. I didn't know about vars(). – Eric Smith Aug 06 '19 at 23:14
16

You'll need to use a metaclass for this:

property = 'foo'
value = 'bar'

class MC(type):
  def __init__(cls, name, bases, dict):
    setattr(cls, property, value)
    super(MC, cls).__init__(name, bases, dict)

class C(object):
  __metaclass__ = MC

print C.foo
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • How do i pass parameter to the file like this `a = MC(params)` ( mc.py is a particular file), `__metaclass__ = MC(params)` doesn't work? – TomSawyer Oct 07 '17 at 07:47
  • Unless `MC` is a callable that returns a metaclass, you don't. And the metaclass will only be set the first time the class is defined so this isn't a way of setting a dynamic metaclass. – Ignacio Vazquez-Abrams Oct 07 '17 at 07:48
  • @TomSawyer: There are at least a couple of ways I know of to pass arguments to a metaclass. Suggest you search here for questions and answers about the subject. – martineau Jun 17 '18 at 21:15
  • use `class C(object, metaclass=MC): pass` in python3 – zhy Mar 30 '21 at 09:24
9

This may be because the class A is not fully initialized when you do your setattr(A, p, v) there.

The first thing to try would be to just move the settattr down to after you close the class block and see if that works, e.g.

class A(object):
    pass

setattr(A, property, value)

Otherwise, that thing Ignacio just said about metaclasses.

Community
  • 1
  • 1
keturn
  • 4,780
  • 3
  • 29
  • 40
  • This solution works very well. There is no need to do this in a metaclass when you can do it directly. – Mike Graham Mar 26 '10 at 01:19
  • 1
    Although it might seem (very slightly) more complicated, as @Vitalik Verhovodov shows in [his answer](https://stackoverflow.com/a/20608050/355230), you can use `vars()` **within** the class body itself to get access to the class being defined's `__dict__`. One you have that, you're pretty much free to modify its contents easily. – martineau Jul 06 '18 at 02:37
4

So I know this is really old and probably beating a dead horse and this may not have been possible at the time but I cam across this trying to solve my own problem.

I realized this can be accomplished without metaclassing.

The setattr takes and object, accessor name, and value. Well the object is not the class name it's the specific instance of the class, which can be accomplished with self.

class A(object):
    def __init__(self):
        self.a = 'i am a accessor'
        setattr(self, 'key', 'value')

a = A()
print a.a
print a.key
StoneyD
  • 306
  • 2
  • 10
  • 2
    Your code is setting an instance attribute, which isn't the same as defining a class attribute—since `self` is an instance of class `A` not the class itself. In other words, it doesn't answer the question being asked. – martineau Jun 17 '18 at 20:52