2010-07-20 16 views
15

Mam klasę Python, która ma atrybuty o nazwie: date1, date2, date3, itp.Jak dynamicznie komponować i uzyskiwać dostęp do atrybutów klas w Pythonie?

W czasie wykonywania, mam zmienną i, która jest liczbą całkowitą.

Co chcę zrobić, to uzyskać dostęp do odpowiedniego atrybutu daty w czasie wykonywania na podstawie wartości i.

Na przykład

gdybym == 1, chcę, aby uzyskać dostęp myobject.date1

gdybym == 2, chcę, aby uzyskać dostęp myobject.date2

I chcę zrobić coś podobnego do klasy zamiast atrybutu.

Na przykład mam kilka klas: MyClass1, MyClass2, MyClass3, itp. I mam zmienną k.

jeśli k == 1, chcę instancję nową instancję MyClass1

jeśli k == 2, chcę instancję nową instancję MyClass2

W jaki sposób można to zrobić?

EDIT

Mam nadzieję unikać giganta if-then-else, aby wybrać odpowiedni atrybut/klasę.

Czy istnieje sposób w języku Python do komponowania nazwy klasy w locie przy użyciu wartości zmiennej?

+2

Proszę edytować to pytanie. Przeczytaj wskazówki dotyczące formatowania po prawej stronie. Proszę poprawnie sformatować swój kod. –

+1

Proszę sformatować to pytanie poprawnie. Chcę upublicznić to jako przydatne pytanie, ponieważ jest to bardzo ładny przykład metaprogramowania i introspekcji. Ale wymaga edycji. –

Odpowiedz

19

Można użyć getattr() dostęp do nieruchomości, gdy nie znać swoją nazwę aż do czasu wykonywania:

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

Jeśli trzymać numerowane zajęcia w module o nazwie foo, można użyć getattr() ponownie do nich dostęp przez liczbę.

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

Powiedziawszy to wszystko, to naprawdę powinni unikać tego typu rzeczy, bo nigdy nie będzie w stanie dowiedzieć się, gdzie są wykorzystywane te numerowane właściwości i klas z wyjątkiem czytania całego kodzie. Lepiej jest umieścić wszystko w słowniku.

+4

Muszę podkreślić, że ostatni akapit z dużym, czerwonym znacznikiem. Na końcu są 2+ identyfikatory o tej samej nazwie i numer kolejny? Zmodyfikuj to. Oznacza to, że jeśli nie masz powodu, który jest dwa razy tak dobry, jak to trzeba uznać za dobry powód. – delnan

+0

Całkowicie uzgodnione. Biorąc pod uwagę wybór, nigdy nie pozwoliłbym, aby taki kod był pozytywny. – Daenyth

0

Dla pierwszego przykładu dodałem metodę instancji na klasie.

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

Na drugim bym wykorzystać funkcję fabryczne:

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

    # etc... 
+0

Cóż, staram się uniknąć gigantycznej instrukcji if-then-else. – Continuation

4

OK, cóż ... Wydaje się, że to wymaga trochę pracy. Po pierwsze, dla twojej daty * rzeczy, powinny być one przechowywane jako dyktando atrybutów. np. myobj.dates[1], i tak dalej.

Dla klas brzmi to tak, jakbyś chciał polimorfizmu. Wszystkie twoje klasy MyClass * powinny mieć wspólnego przodka. Metoda przodka powinna określać, które z jej dzieci mają tworzyć instancję.

Jednym ze sposobów, aby rodzic wiedział, co zrobić, to zachować dyktando dzieci. Istnieją sposoby, że klasa nadrzędna nie musi wyliczać swoich dzieci, wyszukując wszystkie jej podklasy, ale jest to nieco bardziej skomplikowane do wdrożenia. Aby uzyskać więcej informacji na temat tego, jak to zrobić, zapoznaj się z here. Przeczytaj komentarze specjalnie, rozwijają się na nim.

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 

Po trzecie, należy ponownie przemyśleć nazwę zmiennej. Nie używaj liczb do wyliczenia zmiennych, zamiast tego nadaj im znaczące nazwy. i i k są złe w użyciu, ponieważ są zarezerwowane dla indeksów pętli.

Próba istniejącego kodu byłaby bardzo pomocna w jego poprawie.

+0

W jaki sposób metoda __new__ przodka może określić, które dzieci mają tworzyć instancję, bez odwoływania się do dużej instrukcji if-then-else? Mam nadzieję, że użyję wartości zmiennej, aby bezpośrednio "skomponować" nazwę klasy w locie. Czy to możliwe w Pythonie? – Continuation

+0

@ Kontynuacja: zaktualizowałem swoją odpowiedź, aby dodać więcej informacji. – Daenyth

+0

Mam przypadek, w którym atrybuty nazwaInfo, groupInfo itd. Pochodzą z aplikacji vSphere api i do czego mają uzyskać dostęp dynamicznie/programowo w pętli iteracyjnej, a następnie słownik nie działa ". t całkiem mi się przydaje – Hvisage

5

W pierwszym przypadku, powinieneś być w stanie to zrobić:

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

W drugim przypadku można zrobić:

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

Jednakże fakt, że trzeba to zrobić w pierwsze miejsce może być oznaką, że podchodzisz do problemu w sposób bardzo nie-Pythoniczny.

2

zgadzam się z Daenyth, ale jeśli czujesz Sassy można użyć dict metodę, która pochodzi z wszystkich klas:

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

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

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

aby uzyskać listę wszystkich atrybutów, spróbuj:

dir(<class instance>) 
Powiązane problemy