Jak mogę utworzyć obiekt i dodać do niego atrybuty?


310

Chcę utworzyć obiekt dynamiczny (wewnątrz innego obiektu) w Pythonie, a następnie dodać do niego atrybuty.

Próbowałem:

obj = someobject
obj.a = object()
setattr(obj.a, 'somefield', 'somevalue')

ale to nie zadziałało.

Jakieś pomysły?

edytować:

Ustawiam atrybuty z forpętli, która przechodzi przez listę wartości, np

params = ['attr1', 'attr2', 'attr3']
obj = someobject
obj.a = object()

for p in params:
   obj.a.p # where p comes from for loop variable

W powyższym przykładzie chciałbym uzyskać obj.a.attr1, obj.a.attr2, obj.a.attr3.

Użyłem tej setattrfunkcji, ponieważ nie wiedziałem, jak to zrobić obj.a.NAMEz forpętli.

Jak ustawić atrybut na podstawie wartości z ppowyższego przykładu?


4
Co rozumiesz przez „nie działał”? Zakładam, że zgłosił wyjątek AttributeError, prawda?
— Josh Wright

1
Tak. Obiekt „object” nie ma atrybutu „somefield”
— Jan

6
Dlaczego to robisz? Ogólny „obiekt” nie ma rzeczywistego znaczenia . Jakie jest znaczenie tego, co tworzysz? Dlaczego nie jest to odpowiednia klasa ani nazwa-nazwa?
— S.Lott

1
Ten przykład nie jest dla mnie minimalny i mylący, bo po prostu nie rozumiem, dlaczego z niektórymi nie pracujesz a = object() i potrzebujesz obj.a = object(). Ponownie mówię o przykładzie, w twoim rzeczywistym kodzie może się przydać obiekt wewnątrz obiektu.
— kon psych

Odpowiedzi:


215

Możesz użyć mojej starożytnej receptury Bunch , ale jeśli nie chcesz tworzyć „klasy pęczka”, w Pythonie istnieje już bardzo prosta - wszystkie funkcje mogą mieć dowolne atrybuty (w tym funkcje lambda). Tak więc następujące prace:

obj = someobject
obj.a = lambda: None
setattr(obj.a, 'somefield', 'somevalue')

To, czy utrata jasności w porównaniu z czcigodnym Bunchprzepisem jest w porządku, jest decyzją stylową, którą oczywiście podejmę.


25
@FogleBird, decyzja o stylu, jak wspomniałem. Niektórzy eksperci CS wyszkoleni np. W rachunku lambda Kościoła są przyzwyczajeni do myślenia o funkcjach (lambda) jako podstawowym typie wszystkich danych (na przykład liczbę całkowitą 23 można uznać za równoważną lambda: 23), więc do takich ekspertów używających lambdas do tego celu prawdopodobnie nie czułby się niczym „hack”. Osobiście nie bardzo lubię lambda w Pythonie - ale to kwestia osobistego gustu.
— Alex Martelli

A w niektórych przypadkach rozważenie, czy lambdawzorzec pasuje do twojego przypadku użycia, może doprowadzić do uświadomienia sobie, że coś, co pierwotnie uważałeś za dane, jest w rzeczywistości bardziej jak funkcja - lub, w każdym razie, funktorem.
— Kyle Strand

5
@ naught101, funkcja jest obiektem w Pythonie, więc twój sprzeciw jest niezgłębiony.
— Alex Martelli,

6
@ naught101, unikanie tworzenia nowego typu (ponowne użycie istniejącego) nie komplikuje, upraszcza. W dzisiejszych czasach może faktycznie wolę, from argparse import Namespacechoć chciałbym, żeby mieszkał gdzie indziej * np. collection) - ponownie wykorzystując już istniejący typ, po prostu lepszy i wciąż unikając tworzenia nowego typu. Ale wtedy go tam nie było :-).
— Alex Martelli

1
Zobacz odpowiedź poniżej z „JF Sebastian” dotyczącą SimpleNamespace z modułu typów. Jeśli twoja wersja Pythona obsługuje to, jest to najlepsze rozwiązanie (i dokładnie do czego przeznaczona jest SimpleNamespace)
— Tim Richardson

333

Wbudowane objectmogą być tworzone instancje, ale nie mogą mieć ustawionych żadnych atrybutów. (Chciałbym, żeby mógł, właśnie w tym celu.) Nie ma__dict__ posiadać atrybutów.

Zasadniczo po prostu to robię:

class Object(object):
    pass

a = Object()
a.somefield = somevalue

Kiedy mogę, daję Object nadaję klasie bardziej znaczącą nazwę, w zależności od tego, jakie dane w niej umieszczam.

Niektóre osoby robią coś innego, wykorzystując ich podklasę, dictktóra umożliwia dostęp do atrybutów w celu uzyskania kluczy. ( d.keyzamiast d['key'])

Edycja : Do dodania do pytania użycie setattrjest w porządku. Po prostu nie możesz używać setattrw object()instancjach.

params = ['attr1', 'attr2', 'attr3']
for p in params:
    setattr(obj.a, p, value)

9
to może być instancja, po prostu nie służy do niczego przydatne, gdy to zostało zrobione. foo = object()działa, ale po prostu niewiele można z tym zrobić
— Daniel DiPaolo

Cześć. Dziękuję za odpowiedź. Zaktualizowałem swój problem powyżej. zobacz edycję. znasz odpowiedź na to pytanie?
— Jan

przepraszam, nadal chcę ustawić go na obiekcie. patrz aktualizacja powyżej.
— Jan

Naprawdę podoba mi się twoja odpowiedź i myślę, że skręcę w tym kierunku. W tym poście wykorzystałem wszystko inne oprócz tej bardzo prostej, zrozumiałej i czytelnej metodologii. Używanie type....lub lambda nigdy nie było moją ulubioną, podobnie jak wymioty tekstowe w moim kodzie. Ale ten pomysł jest świetny do używania obiektów do przechowywania właściwości. Pozostawia kod bardziej czytelny b / c, gdy widzę lambda Zwalniam czytanie do 25%, podczas gdy twoja droga ma sens! Dzięki.
— Marc

świetna odpowiedź, jedyną rzeczą, którą zmieniłem, było użycie Structnazwy klasy, aby uczynić ją bardziej oczywistą. Zaoszczędził mi mnóstwo pisania ["i "], Na zdrowie!
— pragman

137

W types.SimpleNamespacePython 3.3+ istnieje klasa :

obj = someobject
obj.a = SimpleNamespace()
for p in params:
    setattr(obj.a, p, value)
# obj.a.attr1

collections.namedtuple, typing.NamedTuplemoże być stosowany do obiektów niezmiennych. PEP 557 - Klasy danych sugerują zmienną alternatywę.

Aby uzyskać bogatszą funkcjonalność, możesz wypróbować attrspakiet . Zobacz przykładowe użycie .


3
Jeśli potrzebujesz czegoś, co działa z Pythonem 2.7, możesz także wypróbować argparse.Namespaceklasę
— RolKau

Zgadzam się - byłbym ciekawy, czy istnieje tutaj wada, ale jest to niezwykle przydatna afordancja pytona 3.3+.
— ghukill

cholera! to nie jest dostępne w wersji 2.7?
— Roel,

attrsPakiet @Roel obsługuje Python 2.7
— jfs

Wydaje mi się to lepszym rozwiązaniem niż unittest.mock; ten ostatni jest nieco zbyt ciężki i bardziej plastyczny. W przypadku fałszywego obiektu po prostu przypisanie do atrybutu spowoduje jego powstanie; SimpleNamespace się temu oprze.
— jdzions

32

Istnieje kilka sposobów na osiągnięcie tego celu. Zasadniczo potrzebujesz obiektu, który można rozszerzyć.

obj.a = type('Test', (object,), {})  
obj.a.b = 'fun'  

obj.b = lambda:None

class Test:
  pass
obj.c = Test()

14
obj.a = type('', (), {})
— iman


21

Teraz możesz zrobić (nie jestem pewien, czy to ta sama odpowiedź, co zło):

MyObject = type('MyObject', (object,), {})
obj = MyObject()
obj.value = 42

Odpowiedź @ evilpie ustawia atrybuty bezpośrednio na MyObject (klasa), a nie na instancję taką jak twoja.
— jfs

18

Wypróbuj poniższy kod:

$ python
>>> class Container(object):
...     pass 
...
>>> x = Container()
>>> x.a = 10
>>> x.b = 20
>>> x.banana = 100
>>> x.a, x.b, x.banana
(10, 20, 100)
>>> dir(x)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', 
'__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__',     '__sizeof__', 
'__str__', '__subclasshook__', '__weakref__', 'a', 'b', 'banana']

1
Czy możesz wyjaśnić więcej na temat tego, co to robi? chociaż kod może być przydatny do rozwiązania tego problemu, wyjaśnienie go może znacznie wykraczać poza jeden problem.
— DeadChex,

1
@DeadChex Wyraźnie tworzy nową klasę (obiekt), która jest pustą klasą z właściwościami obiektu, i przechowuje atrybuty wewnątrz klasy. Jest to nawet lepsze niż instalowanie większej liczby modułów lub poleganie na lambdach.
— m3nda

2
Nie jestem pewien, dlaczego nie ma więcej pozytywnych opinii. Czy istnieje powód, aby nie używać tego dla podstawowej klasy kontenera? Wygląda na to, że działa dobrze w Pythonie 2.7, 2.6 i 3.4
— użytkownik5359531

17

Możesz także użyć bezpośrednio obiektu klasy; tworzy przestrzeń nazw:

class a: pass
a.somefield1 = 'somevalue1'
setattr(a, 'somefield2', 'somevalue2')

9

jak mówią doktorzy :

Uwaga : objectnie nie mają __dict__, więc można nie przypisze arbitralnych atrybutów do instancji objectklasy.

Możesz po prostu użyć instancji klasy fikcyjnej.


2

Te rozwiązania są bardzo pomocne podczas testowania. Opierając się na odpowiedziach innych osób, robię to w Pythonie 2.7.9 (bez metody static otrzymuję TypeError (niezwiązana metoda ...):

In [11]: auth = type('', (), {})
In [12]: auth.func = staticmethod(lambda i: i * 2)
In [13]: auth.func(2)
Out[13]: 4

1

Z jakich obiektów korzystasz? Właśnie wypróbowałem to na przykładowej klasie i działało dobrze:

class MyClass:
  i = 123456
  def f(self):
    return "hello world"

b = MyClass()
b.c = MyClass()
setattr(b.c, 'test', 123)
b.c.test

I dostałem 123jako odpowiedź.

Jedyną sytuacją, w której widzę ten błąd, jest próba setattrużycia wbudowanego obiektu.

Aktualizacja: Z komentarza jest to powtórzenie: Dlaczego nie możesz dodawać atrybutów do obiektu w pythonie?


bc jest ustawiony na object (), a nie na zdefiniowaną klasę
— John

0

Przybywam do późna w ciągu dnia, ale tutaj jest moja grosza z obiektem, który akurat zawiera pewne przydatne ścieżki w aplikacji, ale możesz go dostosować do dowolnego miejsca, w którym chcesz uzyskać swoisty słownik informacji, do których możesz uzyskać dostęp za pomocą notacji getattr i kropki (o czym myślę, że tak naprawdę chodzi o to pytanie):

import os

def x_path(path_name):
    return getattr(x_path, path_name)

x_path.root = '/home/x'
for name in ['repository', 'caches', 'projects']:
    setattr(x_path, name, os.path.join(x_path.root, name))

To jest fajne, ponieważ teraz:

In [1]: x_path.projects
Out[1]: '/home/x/projects'

In [2]: x_path('caches')
Out[2]: '/home/x/caches'

Używa to obiektu funkcyjnego jak powyższe odpowiedzi, ale używa funkcji do uzyskania wartości (nadal możesz używać, (getattr, x_path, 'repository')a nie x_path('repository')wolisz).


0

Jeśli potrafimy ustalić i zsumować wszystkie atrybuty i wartości razem przed utworzeniem zagnieżdżonego obiektu, moglibyśmy stworzyć nową klasę, która pobiera argument słownika podczas tworzenia.

# python 2.7

class NestedObject():
    def __init__(self, initial_attrs):
        for key in initial_attrs:
            setattr(self, key, initial_attrs[key])

obj = someobject
attributes = { 'attr1': 'val1', 'attr2': 'val2', 'attr3': 'val3' }
obj.a = NestedObject(attributes)
>>> obj.a.attr1
'val1'
>>> obj.a.attr2
'val2'
>>> obj.a.attr3
'val3'

Możemy również zezwolić na argumenty słów kluczowych. Zobacz ten post .

class NestedObject(object):
    def __init__(self, *initial_attrs, **kwargs):
        for dictionary in initial_attrs:
            for key in dictionary:
                setattr(self, key, dictionary[key])
        for key in kwargs:
            setattr(self, key, kwargs[key])


obj.a = NestedObject(attr1='val1', attr2='val2', attr3= 'val3')

-2
di = {}
for x in range(20):
    name = '_id%s' % x
    di[name] = type(name, (object), {})
    setattr(di[name], "attr", "value")

-2

Innym sposobem widzę, w ten sposób:

import maya.cmds

def getData(objets=None, attrs=None):
    di = {}
    for obj in objets:
        name = str(obj)
        di[name]=[]
        for at in attrs:
            di[name].append(cmds.getAttr(name+'.'+at)[0])
    return di

acns=cmds.ls('L_vest_*_',type='aimConstraint')
attrs=['offset','aimVector','upVector','worldUpVector']

getData(acns,attrs)

możesz dodać w dołączać nazwę attr this di [name] .append ([at, cmds.getAttr (name + '.' + at) [0]])
— Pablo Emmanuel De Leo

1
Dodaje to bardzo dużą niestandardową zależność, a prosty class a: passdaje całą wymaganą moc.
— Alexis Paques,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.