Как динамически компоновать и получать доступ к атрибутам класса в 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...