Wie kann ich ein Objekt erstellen und ihm Attribute hinzufügen?

310

Ich möchte ein dynamisches Objekt (innerhalb eines anderen Objekts) in Python erstellen und ihm dann Attribute hinzufügen.

Ich habe es versucht:

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

aber das hat nicht funktioniert.

Irgendwelche Ideen?

bearbeiten:

Ich setze die Attribute aus einer forSchleife, die eine Liste von Werten durchläuft, z

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

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

In dem obigen Beispiel , das ich bekommen würde obj.a.attr1, obj.a.attr2, obj.a.attr3.

Ich habe die setattrFunktion verwendet, weil ich nicht wusste, wie man mit obj.a.NAMEeiner forSchleife umgeht.

Wie würde ich das Attribut basierend auf dem Wert von pim obigen Beispiel festlegen ?

John
quelle
4
Was meinst du mit "hat nicht funktioniert"? Ich gehe davon aus, dass eine AttributeError-Ausnahme ausgelöst wurde, oder?
Josh Wright
1
Ja. 'Objekt' Objekt hat kein Attribut 'Somefield'
John
6
Warum tust du das? Ein generisches "Objekt" hat keine tatsächliche Bedeutung . Was bedeutet das, was du erschaffst? Warum ist es keine richtige Klasse oder ein benanntes Tupel?
S.Lott
1
Das Beispiel ist nicht minimal und verwirrend für mich oder ich verstehe einfach nicht, warum Sie nicht mit einigen arbeiten a = object()und Sie brauchen obj.a = object(). Ich spreche wieder über das Beispiel, in Ihrem eigentlichen Code könnte ein Objekt innerhalb eines Objekts nützlich sein.
Kon Psych

Antworten:

215

Sie könnten mein altes Bunch- Rezept verwenden, aber wenn Sie keine "Bündelklasse" erstellen möchten, gibt es in Python bereits eine sehr einfache - alle Funktionen können beliebige Attribute haben (einschließlich Lambda-Funktionen). Also funktioniert folgendes:

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

Ob der Verlust an Klarheit im Vergleich zum ehrwürdigen BunchRezept in Ordnung ist, ist eine Stilentscheidung, die ich natürlich Ihnen überlassen werde.

Alex Martelli
quelle
25
@FogleBird, eine Stilentscheidung, wie ich schon sagte. Einige CS-Experten, die z. B. in der Lambda-Rechnung der Kirche geschult sind, sind es gewohnt, Funktionen (Lambdas) als den Grundtyp aller Daten zu betrachten (die ganze Zahl 23 kann beispielsweise als äquivalent zu angesehen werden lambda: 23), so dass solche Experten lambdas für diesen Zweck verwenden würde sich vermutlich nicht wie "ein Hack" anfühlen. Persönlich mag ich lambda Python nicht sehr - aber das ist eine Frage des persönlichen Geschmacks.
Alex Martelli
In einigen Fällen kann die Überlegung, ob das lambdaMuster für Ihren Anwendungsfall geeignet ist oder nicht, dazu führen, dass Sie feststellen, dass etwas, das Sie ursprünglich als Daten angesehen hatten, ohnehin eher einer Funktion ähnelt - oder auf jeden Fall einem Funktor.
Kyle Strand
5
@ naught101, eine Funktion ist in Python ein Objekt, daher ist Ihr Einwand unergründlich.
Alex Martelli
6
@ naught101 Das Vermeiden der Erstellung eines neuen Typs (Wiederverwendung eines vorhandenen Typs) ist nicht kompliziert, sondern vereinfacht. Heutzutage könnte es tatsächlich vorziehen, from argparse import Namespaceobwohl ich wünschte, es würde woanders leben * zB collection) - wieder einen jetzt existierenden Typ wiederverwenden, nur einen besseren, und trotzdem die Erstellung neuer Typen vermeiden. Aber es war damals nicht da :-).
Alex Martelli
1
Siehe Antwort unten von "JF Sebastian" bezüglich SimpleNamespace aus dem Typmodul. Wenn Ihre Python-Version dies unterstützt, ist dies die beste Lösung (und genau das, wofür SimpleNamespace entwickelt wurde)
Tim Richardson
334

Das integrierte objectGerät kann instanziiert werden, es können jedoch keine Attribute festgelegt werden. (Ich wünschte, es könnte genau zu diesem Zweck möglich sein.) Es muss keine __dict__Attribute enthalten.

Ich mache im Allgemeinen einfach Folgendes:

class Object(object):
    pass

a = Object()
a.somefield = somevalue

Wenn ich kann, gebe ich der ObjectKlasse einen aussagekräftigeren Namen, je nachdem, welche Art von Daten ich in sie einfüge.

Einige Leute machen eine andere Sache, indem sie eine Unterklasse verwenden dict, die den Zugriff auf Attribute auf die Schlüssel ermöglicht. ( d.keystatt d['key'])

Bearbeiten : Für die Ergänzung Ihrer Frage ist die Verwendung setattrin Ordnung. Sie können es einfach nicht setattrfür object()Instanzen verwenden.

params = ['attr1', 'attr2', 'attr3']
for p in params:
    setattr(obj.a, p, value)
FogleBird
quelle
9
Es kann instanziiert werden und wird erst dann für nützliche Zwecke verwendet. foo = object()funktioniert, aber man kann einfach nicht viel damit
anfangen
Hallo. Danke für die Antwort. Ich habe mein Problem oben aktualisiert. siehe die Bearbeitung. Kennst du die Antwort darauf?
John
Entschuldigung, ich möchte es immer noch auf das Objekt setzen. siehe Update oben.
John
Ihre Antwort gefällt mir sehr gut und ich denke, ich werde diese Richtung in Zukunft einschlagen. Ich habe fast alles andere in diesem Beitrag verwendet, außer dieser sehr einfachen, verständlichen und lesbaren Methodik. Verwenden type....oder Lambda war nie mein Favorit, wie Text Erbrechen in meinem Code. Diese Idee eignet sich jedoch hervorragend für die Verwendung von Objekten zum Speichern von Eigenschaften. Lässt Code besser lesbar, wenn ich Lambda sehe. Ich verlangsame meinen Lesevorgang auf 25%, während Ihr Weg absolut sinnvoll ist! Vielen Dank.
Marc
Tolle Antwort, das einzige, was ich geändert habe, war, Structden Namen der Klasse zu verwenden, um es offensichtlicher zu machen. Ersparte mir eine Menge Tippen ["und "], Prost!
Pragman
137

types.SimpleNamespaceIn Python 3.3+ gibt es eine Klasse :

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

collections.namedtuple, typing.NamedTuplekönnte für unveränderliche Objekte verwendet werden. PEP 557 - Datenklassen schlagen eine veränderbare Alternative vor.

Für eine umfassendere Funktionalität können Sie das Paket ausprobierenattrs . Siehe ein Beispiel für die Verwendung .

jfs
quelle
3
Wenn Sie etwas brauchen, das mit Python 2.7 funktioniert, können Sie auch die argparse.NamespaceKlasse ausprobieren
RolKau
Einverstanden - Ich wäre gespannt, ob es hier einen Nachteil gibt, aber dies ist ein unglaublich praktischer Python 3.3+ -Vorteil.
Ghukill
Verdammt! Dies ist nicht verfügbar auf 2.7?
Roel
@ Roel- attrsPaket unterstützt Python 2.7
jfs
Dies scheint mir eine bessere Lösung zu sein als unittest.mock; Letzteres ist etwas zu schwer und etwas formbarer. Wenn Sie ein Scheinobjekt einfach einem Attribut zuweisen, entsteht es. SimpleNamespace wird dem widerstehen.
jdzions
32

Es gibt einige Möglichkeiten, um dieses Ziel zu erreichen. Grundsätzlich benötigen Sie ein Objekt, das erweiterbar ist.

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

obj.b = lambda:None

class Test:
  pass
obj.c = Test()
Evilpie
quelle
14
obj.a = type('', (), {})
Iman
27

Das mockModul ist grundsätzlich dafür gemacht.

import mock
obj = mock.Mock()
obj.a = 5
Dunatotatos
quelle
3
Nachteil ist, dass dies eine externe Abhängigkeit ist
Kangur
5
unittest.Mockist ein Teil der Standardbibliothek seit Python 3.3 ( docs.python.org/3/library/unittest.mock.html )
illagrenan
2
Hängt von der Verwendung Ihres Codes ab, denke ich. Wenn es Produktionscode ist, würde ich nicht einige mockdarin wollen. Fühlt sich für mich einfach komisch an.
Mike de Klerk
21

Jetzt können Sie Folgendes tun (nicht sicher, ob es die gleiche Antwort wie bei Evilpie ist):

MyObject = type('MyObject', (object,), {})
obj = MyObject()
obj.value = 42
andreabedini
quelle
Die Antwort von @ evilpie legt Attribute direkt auf MyObject (die Klasse) fest, nicht auf die Instanz wie Ihre.
JFS
18

Versuchen Sie den folgenden Code:

$ 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']
neldor
quelle
1
Können Sie mehr darüber erklären, was dies bewirkt? Während der Code zur Lösung dieses Problems nützlich sein kann, kann seine Erklärung viel weiter gehen als nur ein Problem.
DeadChex
1
@DeadChex Es wird eindeutig eine neue Klasse (Objekt) erstellt, bei der es sich um eine leere Klasse mit Objekteigenschaften handelt, und die Attribute werden in der Klasse gespeichert. Dies ist sogar besser, als mehr Module zu installieren oder sich auf Lambdas zu verlassen.
m3nda
2
Ich bin mir nicht sicher, warum dies nicht mehr positive Stimmen hat. Gibt es einen Grund, dies nicht für eine grundlegende Containerklasse zu verwenden? Scheint in Python 2.7, 2.6 und 3.4 gut zu
funktionieren
17

Sie können ein Klassenobjekt auch direkt verwenden. Es wird ein Namespace erstellt:

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

wie die Dokumente sagen :

Hinweis : objecthat kein a __dict__, daher können Sie einer Instanz der objectKlasse keine beliebigen Attribute zuweisen .

Sie könnten einfach eine Dummy-Klasseninstanz verwenden.

SilentGhost
quelle
2

Diese Lösungen sind beim Testen sehr hilfreich. Aufbauend auf den Antworten aller anderen mache ich dies in Python 2.7.9 (ohne statische Methode erhalte ich einen TypeError (ungebundene Methode ...):

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

Welche Objekte verwenden Sie? Habe das gerade mit einer Beispielklasse versucht und es hat gut funktioniert:

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

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

Und ich bekam 123als Antwort.

Die einzige Situation, in der ich sehe, dass dies fehlschlägt, ist, wenn Sie es versuchen setattr mit einem eingebauten Objekt .

Update: Aus dem Kommentar geht hervor, dass Folgendes wiederholt wird: Warum können Sie dem Objekt in Python keine Attribute hinzufügen?

jneves
quelle
bc ist auf object () gesetzt, keine definierte Klasse
John
0

Ich komme zu spät am Tag, aber hier ist mein Pennyworth mit einem Objekt, das zufällig einige nützliche Pfade in einer App enthält, aber Sie können es für alles anpassen, wo Sie eine Art Diktat von Informationen wünschen, auf die Sie mit getattr und Punktnotation zugreifen können (worum es meiner Meinung nach bei dieser Frage wirklich geht):

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

Das ist cool, weil jetzt:

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

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

Dies verwendet also das Funktionsobjekt wie die obigen Antworten, verwendet jedoch die Funktion, um die Werte abzurufen (Sie können es weiterhin verwenden, (getattr, x_path, 'repository')anstatt x_path('repository')es zu bevorzugen).

Paul Whipp
quelle
0

Wenn wir alle Attribute und Werte zusammen bestimmen und aggregieren können, bevor wir das verschachtelte Objekt erstellen, können wir eine neue Klasse erstellen, die beim Erstellen ein Wörterbuchargument verwendet.

# 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'

Wir können auch Schlüsselwortargumente zulassen. Siehe diesen Beitrag .

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')
HarlemSquirrel
quelle
-2
di = {}
for x in range(20):
    name = '_id%s' % x
    di[name] = type(name, (object), {})
    setattr(di[name], "attr", "value")
lmokto
quelle
-2

Anders sehe ich das so:

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)
Pablo Emmanuel De Leo
quelle
Sie können den Namen attr this di [name] .append ([at, cmds.getAttr (name + '.' + at) [0]] anhängen
Pablo Emmanuel De Leo
1
Dies fügt eine sehr große nicht standardmäßige Abhängigkeit hinzu, während eine einfache class a: passdie gesamte erforderliche Leistung bietet.
Alexis Paques