2010-02-17 2 views
22

Hey, ich habe gerade angefangen darüber nachzudenken, als ich auf einen Code stieß, der ein Objekt mit bestimmten Attributen erwartete (aber ohne Angabe, welchen Typ dieses Objekt haben sollte).Kürzeste Möglichkeit, ein Objekt mit beliebigen Attributen in Python zu erstellen?

Eine Lösung wäre, eine neue Klasse mit den Attributen zu erstellen, die der Code erwartet, aber wenn ich anderen Code anrufe, der auch Objekte mit (anderen) Attributen benötigt, müsste ich immer mehr Klassen erstellen.

Eine kürzere Lösung ist eine generische Klasse zu erstellen, und legen Sie dann die Attribute auf Instanzen davon (für diejenigen, die sich für eine Instanz von object anstelle eine neue Klasse zu erstellen, die nicht seit object Instanzen arbeiten don erlaube keine neuen Attribute.

Die letzte, kürzeste Lösung kam ich mit war eine Klasse mit einem Konstruktor zu erstellen, die Keyword-Argumente übernimmt, genau wie der dict Konstruktor, und dann setzt sie als Attribut:

class data: 
    def __init__(self, **kw): 
     for name in kw: 
      setattr(self, name, kw[name]) 

options = data(do_good_stuff=True, do_bad_stuff=False) 

Aber ich kann‘ t Hilf mir das Gefühl zu haben, etwas Offensichtliches verpasst zu haben ... Gibt es dafür keine integrierte Methode (vorzugsweise in Python 2.5 unterstützt)?

+4

Jeder Python (mit Ausnahme einiger goofballs die stdlib schreiben) Namen ihren Klassen mit Großgeschriebene Wörter, die den Leuten helfen zu erkennen, welche Namen sie repräsentieren. Außerdem ist es eine gute Angewohnheit, immer von "Objekt" zu erben, also verwenden Sie * neue Stilklassen *. –

+0

Ja, ich wollte, dass die Klasse wie ein Datentyp aussieht, da sie keine Klassenfunktionalität hat (was auch der Grund ist, warum ich bewusst keine neuen Stilklassen verwende, da ich keine Methodenauflösungsreihenfolge verwenden kann). Aber die Klasse sollte wahrscheinlich einen großgeschriebenen Namen haben, du hast Recht. – Blixt

Antwort

18

Der ursprüngliche Code kann mit __dict__ ein wenig rationalisiert werden:

In [1]: class data: 
    ...:  def __init__(self, **kwargs): 
    ...:   self.__dict__.update(kwargs) 
    ...: 

In [2]: d = data(foo=1, bar=2) 

In [3]: d.foo 
Out[3]: 1 

In [4]: d.bar 
Out[4]: 2 
+0

Ich denke, diese Lösung könnte die Bedenken lösen, die Blixt mit S. Lotts Antwort hatte. – cmcginty

16

Verwenden Sie collections.namedtuple.

Es funktioniert gut.

from collections import namedtuple 
Data = namedtuple('Data', [ 'do_good_stuff', 'do_bad_stuff' ]) 
options = Data(True, False) 
+1

Nett, obwohl das in meinem Fall eine Menge unnötiger Funktionalität hinzufügt. Leider ist der meiste Code, den ich schreibe, auf Python 2.5 beschränkt, und namedtuple wurde in 2.6 hinzugefügt. +1 obwohl =) – Blixt

+0

@Blixt: "nicht benötigte Funktionalität"? Na und? Der größte Teil von Python kann als unnötige Funktionalität für Anwendungsfälle bezeichnet werden. Erwägen Sie ein Upgrade. Nichts bricht; Sie erhalten die Anweisung 'with' und die Möglichkeit, die Funktion' print' zu verwenden. –

+1

Ich meinte nur, dass ich mit einer einfacheren Lösung gehen könnte, diese Lösung hat extra Tupel-Funktionalität, die ich nicht wirklich brauche (plus es schafft eine ganz neue Klasse; Ich wollte nur eine Instanz mit den angegebenen Attributen erstellen, die ich kann dann wegwerfen). Ich kann ein Upgrade nicht in Betracht ziehen, da es sich um ein Projekt bei der Arbeit handelt, das strenge Regeln dafür enthält, welche Software verwendet wird. – Blixt

0

Wenn ich Ihre Frage richtig zu verstehen, müssen Sie Datensätze. Python-Klassen können auf diese Weise verwendet werden, was Sie tun.

Ich glaube, die pythonischste Art des Umgangs mit "Aufzeichnungen" ist einfach ... Wörterbücher! Eine Klasse ist eine Art Wörterbuch über Steroide.

Ihr Klassenbeispiel data ist im Wesentlichen eine Möglichkeit, ein Wörterbuch in eine Klasse umzuwandeln.

(Auf einer Seite zur Kenntnis, würde ich eher self.__setattr__(name, kw[name]) verwenden.)

+1

Trotzdem kann man 'd = {'field': 123}' nicht als 'd.field' verwenden, was meiner Meinung nach seine Absicht ist. –

+0

Genau das, was Dejw gesagt hat. Ich hätte es auch vorgezogen, "dict" in diesem speziellen Fall zu verwenden. Ich denke, dass die Verwendung der '__setattr__'-Methode in diesem Fall sinnlos ist, weil sie äquivalent zu' setattr' ist, aber länger, und fügt dem, was gerade passiert, keine Klarheit hinzu. – Blixt

+1

Olivier, Verwenden von '_setattr_ in diesem Fall funktioniert nicht, noch wäre es besser, wenn es tat. –

9

Dies ist der kürzeste Weg, ich weiß

>>> obj = type("myobj",(object,),dict(foo=1,bar=2)) 
>>> obj.foo 
1 
>>> obj.bar 
2 
>>> 

mit dict statt {} versichert Ihre Attributnamen gültig sind

>>> obj = type("myobj",(object,),{"foo-attr":1,"bar-attr":2}) 
>>> obj.foo-attr 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: type object 'myobj' has no attribute 'foo' 
>>> 
+1

Interessante Lösung! Aber was es tatsächlich tut, ist einen neuen Typ zu erzeugen (der instanziiert werden kann, d. H. "O = obj()"). Was Ihr zweites Beispiel betrifft, können Sie * getattr (obj, 'foo-attr') 'tun, aber ich stimme zu, dass Sie diese Art von Namen für Attribute vermeiden sollten. – Blixt

+1

ja, die Tatsache, dass Sie instanziieren können, ist definitiv eine seltsame Nebenwirkung. Ich habe auch Probleme mit der Verwendung von Diktaten, die von JSons deserialisiert wurden, die Unicode-Strings haben, da Schlüssel manchmal die getattr-Maschinerie durcheinander bringen. Es ist nicht kugelsicher, und ich würde es nicht überall verwenden, aber es kann in bestimmten Situationen sehr nützlich sein. –

0

Dies ist in der Regel etwas, das Sie überhaupt eine dict für nicht machen eine Klasse verwenden würde.

+0

Ich stimme zu. Ich arbeite jedoch mit Code außerhalb meiner Kontrolle. – Blixt

13

Dies funktioniert in 2.5, 2.6 und 3.1:

class Struct(object): 
    pass 

something = Struct() 
something.awesome = abs 

result = something.awesome(-42) 

EDIT: Ich dachte, vielleicht die Quelle zu geben und würde helfen.

EDIT: Hinzugefügt Zuordnung zu Ergebnis, wie ich mit den interaktiven Dolmetscher zu überprüfen, und Sie möglicherweise nicht.

6

Verwenden Sie eine Kombination zwischen Lambda und Typ build-in, ich glaube, ist die kleinste Art und Weise, das zu tun:

obj = lambda **kwargs: type('obj', (object,), kwargs)() 

options = obj(do_good_stuff=True, do_bad_stuff=False) 

print options.do_good_stuff 
print options.do_bad_stuff 
2

Wenn Sie keine Werte in den Konstruktor übergeben müssen, können Sie dies tun:

class data: pass 

data.foo = 1 
data.bar = 2 

Sie verwenden die statischen Klassenmitgliedsvariablen, um Ihre Daten zu speichern.

14

type('',(), {})() erstellt ein Objekt, das beliebige Attribute haben kann.

Beispiel:

obj = type('',(), {})() 
obj.hello = "hello" 
obj.world = "world" 
print obj.hello, obj.world #will print "hello world" 

type() mit drei Argumenten eine neue Art erzeugt.

Das erste Argument '' ist der Name des neuen Typs. Der Name interessiert uns nicht, deshalb lassen wir ihn leer. Das zweite Argument () ist ein Tupel von Basistypen, hier object (implizit).

Das dritte Argument ist ein Wörterbuch der Attribute des neuen Objekts - wieder wir kümmern uns nicht um eine leere Wörterbuch ist {}

Und am Ende instanziiert wir eine neue Instanz dieses neuen Typs mit () bei der Ende.

4

Eine Funktion ist ein Objekt. Sie können also einer Funktion Attribute zuweisen. Oder einen machen. Dies ist der einfachste Weg in Bezug auf Codezeilen, denke ich.

def hello(): 
    pass 


hello.chook = 123 

aber die einfachste & eleganteste Art und Weise (aber Python> 3.3) ist Standard-Libary des simpleNamespace verwenden https://docs.python.org/3/library/types.html#types.SimpleNamespace

>>> from types import SimpleNamespace 
>>> foo = SimpleNamespace() 
>>> foo.hello = "world" 
Verwandte Themen