How to dynamically compose and access class attributes in Python?

Question:

I have a Python class that have attributes named: date1, date2, date3, etc.

During runtime, I have a variable i, which is an integer.

What I want to do is to access the appropriate date attribute in run time based on the value of i.

For example,

if i == 1, I want to access myobject.date1

if i == 2, I want to access myobject.date2

And I want to do something similar for class instead of attribute.

For example, I have a bunch of classes: MyClass1, MyClass2, MyClass3, etc. And I have a variable k.

if k == 1, I want to instantiate a new instance of MyClass1

if k == 2, I want to instantiate a new instance of MyClass2

How can i do that?

EDIT

I’m hoping to avoid using a giant if-then-else statement to select the appropriate attribute/class.

Is there a way in Python to compose the class name on the fly using the value of a variable?

Asked By: Continuation

||

Answers:

OK, well… It seems like this needs a bit of work. Firstly, for your date* things, they should be perhaps stored as a dict of attributes. eg, myobj.dates[1], so on.

For the classes, it sounds like you want polymorphism. All of your MyClass* classes should have a common ancestor. The ancestor’s __new__ method should figure out which of its children to instantiate.

One way for the parent to know what to make is to keep a dict of the children. There are ways that the parent class doesn’t need to enumerate its children by searching for all of its subclasses but it’s a bit more complex to implement. See here for more info on how you might take that approach. Read the comments especially, they expand on it.

class Parent(object):
    _children = {
      1: MyClass1,
      2: MyClass2,
    }

    def __new__(k):
        return object.__new__(Parent._children[k])

class MyClass1(Parent):
    def __init__(self):
        self.foo = 1

class MyClass2(Parent):
    def __init__(self):
        self.foo = 2

bar = Parent(1)
print bar.foo # 1
baz = Parent(2)
print bar.foo # 2

Thirdly, you really should rethink your variable naming. Don’t use numbers to enumerate your variables, instead give them meaningful names. i and k are bad to use as they are by convention reserved for loop indexes.

A sample of your existing code would be very helpful in improving it.

Answered By: Daenyth

For the first case, you should be able to do:

getattr(myobject, 'date%s' % i)

For the second case, you can do:

myobject = locals()['MyClass%s' % k]()

However, the fact that you need to do this in the first place can be a sign that you’re approaching the problem in a very non-Pythonic way.

Answered By: Aram Dulyan

I agree with Daenyth, but if you’re feeling sassy you can use the dict method that comes with all classes:

>>> class nullclass(object):
        def nullmethod():
            pass

>>> nullclass.__dict__.keys()
['__dict__', '__module__', '__weakref__', 'nullmethod', '__doc__']

>>> nullclass.__dict__["nullmethod"]
<function nullmethod at 0x013366A8>
Answered By: Aaron Altman

You can use getattr() to access a property when you don’t know its name until runtime:

obj = myobject()
i = 7
date7 = getattr(obj, 'date%d' % i) # same as obj.date7

If you keep your numbered classes in a module called foo, you can use getattr() again to access them by number.

foo.py:
  class Class1: pass
  class Class2: pass
  [ etc ]


bar.py:
  import foo
  i = 3
  someClass = getattr(foo, "Class%d" % i) # Same as someClass = foo.Class3
  obj = someClass() # someClass is a pointer to foo.Class3
  # short version:
  obj = getattr(foo, "Class%d" % i)()

Having said all that, you really should avoid this sort of thing because you will never be able to find out where these numbered properties and classes are being used except by reading through your entire codebase. You are better off putting everything in a dictionary.

Answered By: too much php

to get a list of all the attributes, try:

dir(<class instance>)
Answered By: trevorKirkby
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.