Как динамически компоновать и получать доступ к атрибутам класса в Python?

У меня есть класс Python, у которого есть атрибуты с именем: date1, date2, date3 и т.д.

Во время выполнения у меня есть переменная i, которая является целым числом.

Я хочу сделать доступ к соответствующему атрибуту даты во время выполнения на основе значения i.

Например,

если я == 1, я хочу получить доступ к myobject.date1

если я == 2, я хочу получить доступ к myobject.date2

И я хочу сделать что-то подобное для класса вместо атрибута.

Например, у меня есть куча классов: MyClass1, MyClass2, MyClass3 и т.д. И у меня есть переменная k.

if k == 1, я хочу создать экземпляр нового экземпляра MyClass1

if k == 2, я хочу создать экземпляр нового экземпляра MyClass2

Как я могу это сделать?

ИЗМЕНИТЬ

Я надеюсь избежать использования гигантского оператора if-then-else для выбора соответствующего атрибута/класса.

Есть ли способ в Python составить имя класса на лету, используя значение переменной?

Ответы

Ответ 1

Вы можете использовать getattr() для доступа к свойству, если вы не знаете его имя до времени выполнения:

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

Если вы сохраняете свои пронумерованные классы в модуле с именем foo, вы можете снова использовать getattr() для доступа к ним по номеру.

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)()

Сказав все это, вы на самом деле должны избегать такого рода вещей, потому что никогда не сможете узнать, где эти нумерованные свойства и классы используются, кроме как путем чтения всей вашей кодовой базы. Вам лучше положить все в словарь.

Ответ 2

Хорошо, ну... Кажется, это требует немного работы. Во-первых, для вашей даты * вещи они должны быть, возможно, сохранены как атрибут атрибутов. например, myobj.dates[1] и т.д.

Для классов это похоже на то, что вы хотите полиморфизм. Все ваши классы MyClass * должны иметь общего предка. Метод ancestor __new__ должен выяснить, какой из его дочерних элементов должен создать экземпляр.

Один из способов родителя знать, что делать, это сохранить диктовку детей. Существуют способы, с помощью которых родительский класс не должен перечислить своих дочерних элементов, выполнив поиск всех его подклассов, но немного сложнее реализовать. См. здесь для получения дополнительной информации о том, как вы можете принять этот подход. Читайте комментарии особенно, они расширяются на нем.

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

В-третьих, вы действительно должны переосмыслить свое имя переменной. Не используйте числа для перечисления переменных, вместо этого давайте им значимые имена. i и k являются плохими для использования, поскольку они по соглашению зарезервированы для индексов цикла.

Образец вашего существующего кода будет очень полезен для его улучшения.

Ответ 3

В первом случае вы должны иметь возможность:

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

Во втором случае вы можете:

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

Однако тот факт, что вам нужно сделать это в первую очередь, может быть признаком того, что вы приближаетесь к проблеме очень не-питоническим способом.

Ответ 4

Я согласен с Daenyth, но если вы чувствуете себя нахальным, вы можете использовать метод dict, который поставляется со всеми классами:

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

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

>>> nullclass.__dict__["nullmethod"]
<function nullmethod at 0x013366A8>

Ответ 5

чтобы получить список всех атрибутов, попробуйте:

dir(<class instance>)

Ответ 6

В первом примере я бы добавил метод экземпляра в классе.

def get_date_for_i(self, i):
    if i == 1:
        return self.date1
    elif i == 2:
        return self.date2
    # etc...

Для второго я использовал бы функцию factory:

def get_class_for_k(k):
   if k == 1:
       return MyClass1
   if k == 2:
       return MyClass2

   # etc...