Hat Python anonyme Klassen?

74

Ich frage mich, ob Python so etwas wie die Funktion für anonyme C # -Klassen hat. Zur Verdeutlichung hier ein Beispiel für ein C # -Schnipsel:

var foo = new { x = 1, y = 2 };
var bar = new { y = 2, x = 1 };
foo.Equals(bar); // "true"

In Python würde ich mir so etwas vorstellen:

foo = record(x = 1, y = 2)
bar = record(y = 2, x = 1)
foo == bar  # true

Die spezielle Anforderung besteht darin, ein Objekt mit bestimmten Feldern im Ausdruckskontext erstellen zu können (z. B. in Lambdas und anderen Orten verwendbar, an denen Anweisungen nicht zulässig sind), ohne zusätzliche externe Deklarationen und über das normale Mitglied namentlich auf einzelne Komponenten zugreifen zu können Zugriffssyntax foo.bar. Das erstellte Objekt sollte auch einen strukturellen Vergleich nach Komponentennamen implementieren (nicht nach Position, wie dies bei Tupeln der Fall ist).

Insbesondere: Tupel nicht, weil ihre Komponenten nicht benannt sind; Klassen nicht, weil sie eine Deklaration erfordern; dicts ist es nicht, weil sie unerwünschte foo["bar"]Syntax haben, um auf Komponenten zuzugreifen.

namedtuple ist es nicht, da es auch dann noch einen Namen erfordert, wenn Sie den Typ inline definieren und der Vergleich positionsbasiert und nicht namensbasiert ist. Speziell:

 def foo(): return namedtuple("Foo", "x y")(x = 1, y = 2)
 def bar(): return namedtuple("Foo", "y x")(x = 1, y = 2)
 foo() == bar()   # False because fields are compared in order, and not by name
                  # True would be desired instead

Ich weiß, wie man so etwas bei Bedarf in Python schreibt. Aber ich würde gerne wissen, ob es so etwas in der Python-Standardbibliothek oder in beliebten Bibliotheken von Drittanbietern gibt.

[BEARBEITEN]

Aus diesem Grund gibt es hier eine Lösung mit einem Ausdruck, die zwei sehr informative Antworten von Ken und Alanlcode kombiniert und strukturelle Gleichheit ohne zusätzliche externe Erklärungen ergibt:

type("", (), { \
    "__init__": (lambda self, **kwargs: self.__dict__.update(kwargs)), \
    "__eq__": (lambda self, other: self.__dict__ == other.__dict__) } \
)(x = 1, y = 2)

Technisch erfüllt es alle Anforderungen der Frage, aber ich hoffe aufrichtig, dass niemand es jemals benutzt (ich werde es definitiv nicht tun).

Pavel Minaev
quelle
2
klingt wie Wörterbücher sollten den Job machen. Ich finde es am besten, den Python-Weg zu finden, als eine andere Sprache in Python zu integrieren. Übrigens - wenn Ihnen das Wörterbuch nicht gefällt, können Sie mit der Zugriffsmethode foo ["bar"] alternativ die Methode get verwenden: foo.get ("bar")
monkut
1
Da dies bei Bedarf trivial in Python implementiert werden kann, sehe ich keinen besonderen Grund, dies nicht zu tun, und halte es definitiv nicht für "eine andere Sprache in Python einpassen". Zumal es der bestehenden namedtupleAbsicht ziemlich nahe zu kommen scheint .
Pavel Minaev
2
Ich finde es bizarr, eine Frage zu stellen, ob Sprache X das Merkmal Sprache Y hat, und dann zu verlangen, dass alles genau gleich ist. Sprachen sind nicht genau gleich. Python hat keine anonymen Funktionen, aber sie haben Wörterbücher und sie funktionieren genauso gut. Ja, die Zugriffssyntax ist unterschiedlich. Große verdammte Sache.
Lennart Regebro
3
Ich verlange nicht, dass die Funktion genau gleich ist - wenn ich das täte, würde ich auch nach statischer Typisierung und Unveränderlichkeit fragen;) Ich frage nur nach einer Syntax, die ich als natürlicher und bequemer betrachte.
Pavel Minaev
Ich hatte die Tendenz, Objekte zu erstellen, damit ich am Anfang Daten für den Attributzugriff darauf kleben konnte. Sie werden sich bald an Diktate gewöhnen.
Lennart Regebro

Antworten:

50

Der pythonische Weg wäre, a zu verwenden dict:

>>> foo = dict(x=1, y=2)
>>> bar = dict(y=2, x=1)
>>> foo == bar
True

Erfüllt alle Ihre Anforderungen, außer dass Sie noch tun müssen, foo['x']anstatt foo.x.

Wenn dies ein Problem ist, können Sie leicht eine Klasse definieren wie:

class Bunch(object):
    def __init__(self, **kwds):
        self.__dict__.update(kwds)

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

Oder eine schöne und kurze

class Bunch(dict):
    __getattr__, __setattr__ = dict.get, dict.__setitem__

(aber beachte, dass dieser zweite Probleme hat, wie Alex in seinem Kommentar betont!)

dF.
quelle
6
Aus der Frage - "... diktiert nicht, weil sie eine unerwünschte foo [" bar "] - Syntax für den Zugriff auf Komponenten haben."
zu viel PHP
Dies funktioniert, außer um auf Dinge zuzugreifen, ist es foo ['x'], nicht foo.x
Mike Cooper
3
Trotzdem ist die Anfrage so, wie sie ist, und dies beantwortet sie nicht. Ich sehe auch nicht, wie es "unpythonisch" ist, wenn man bedenkt namedtuple, dass es den größten Teil des syntaktischen Zuckers gibt, den ich will.
Pavel Minaev
12
Ich denke, "Bunch" (erste Form) ist die richtige Antwort (aber ich bin voreingenommen, seit ich den Namen vor mehr als 8 Jahren geprägt habe - siehe code.activestate.com/recipes/52308, der übrigens der Top-Google-Hit für die suche [Haufen Python] ;-). Die zweite Form hat tiefe und etwas subtile Probleme, mache x = Bunch (update = 23) und schau was x.update IST ;-) - du nennst das NICE? -)
Alex Martelli
2
@ Alex: Wie wäre es mit Einstellung __getattribute__ = dict.get? Hässlich, ja, aber hat es immer noch Probleme?
dF.
53

Es sieht so aus, als hätte Python 3.3 genau dieses Ding in Form einer types.SimpleNamespaceKlasse hinzugefügt .

Pavel Minaev
quelle
5
Diese Antwort sollte oben stehen.
Balu
Es gibt so viele versteckte Python-Teile, die wirklich prominenter und in diese Sprache eingebettet sein sollten. Es scheint eine gewisse Zurückhaltung zu geben, die Sprache selbst zu modernisieren, anstatt diese Bolt-Ons hinzuzufügen. Die Antwort hat bei mir funktioniert, ich muss nur jedes Mal darauf zurückkommen, wenn ich es tun möchte!
Mark Adamson
43

1) Siehe http://uszla.me.uk/space/blog/2008/11/06 . Mit der typeintegrierten Funktion können Sie ein anonymes Objekt mit leicht hässlicher Syntax erstellen :

 anon_object_2 = type("", (), {})()

Dabei ist der dritte Parameter das Diktat, das die Felder Ihres Objekts enthält.

 foo = type("", (), dict(y=1))()
 foo.y == 1

2) Eine weitere Variante wird von Peter Norvig unter http://norvig.com/python-iaq.html vorgeschlagen . Es ähnelt auch der Antwort von Ken.

class Struct:
    def __init__(self, **entries): self.__dict__.update(entries)

>>> options = Struct(answer=42, linelen = 80, font='courier')
>>> options.answer
42

Der Vorteil dieser Methode besteht darin, dass Sie die Gleichheit nach Inhalten des Diktats implementieren können, die die erste Option nicht hat.

Alanlcode
quelle
Ich mag die 'struct'-Klasse. Sehr nützlich für mich, da für mein Problem keine speziellen Methoden oder Operatoren erforderlich waren. Kurz gesagt, ich verwende Schaum / Seife und die meiste Zeit erstellt Schaum ein Antwortobjekt für mich, dessen Struktur durch eine WSDL definiert wird. Wenn Sie schlechtes XML erhalten, löst der Saxophon-Parser eine Ausnahme aus, sodass Sie kein Antwortobjekt haben. Ich 'fälsche' ein Antwortobjekt mit der obigen Struct-Klasse (setze nur die Eigenschaften 'error' und 'message' für meine Anwendung) und übergebe dieses Downstream. Wenn die Fehlerbehandlung jemals mehr Eigenschaften (oder Methoden?) Erwartet, ist es einfach, diese Klasse entsprechend hinzuzufügen (zu erweitern?). SIEG.
FlipMcF
6

Das Typformular (...) erfüllt die strukturelle Vergleichsanforderung nicht (ohne wirklich hässlich zu werden). Das Diktatformular (...) erfüllt nicht die Attributzugriffsanforderungen.

Das Attribut scheint irgendwo in die Mitte zu fallen:

class attrdict(dict):
    def __init__(self, *args, **kwargs):
        dict.__init__(self, *args, **kwargs)
        self.__dict__ = self

a = attrdict(x=1, y=2)
b = attrdict(y=2, x=1)

print a.x, a.y
print b.x, b.y
print a == b

Aber es bedeutet, eine spezielle Klasse zu definieren.

OK, ich habe gerade das Update der Frage bemerkt. Ich werde nur bemerken, dass Sie dictfür den Basisparameter angeben können und nur dann den Konstruktor angeben müssen (im Ausdruck icky type). Ich bevorzuge attrdict. :-)

ars
quelle
1
Das Setzen von self .__ dict__ = self führt zu einem Speicherverlust, daher würde ich davon abraten. bugs.python.org/issue1469629
rmmh
Soll der __init__Anruf nicht so aussehen super(attrdict, self).__init__(*args, **kwargs)?
Kasperd
6

Ich erinnere mich nicht ohne weiteres, ob es eine integrierte Funktion gibt, aber das Schreiben selbst ist kürzer als das Eingeben Ihrer Frage. :-)

class record(object):
  def __init__(self, **kwargs): self.__dict__ = kwargs
  def __eq__(self, r2): return self.__dict__ == r2.__dict__
  def __ne__(self, r2): return self.__dict__ != r2.__dict__

foo = record(x=1, y=2)
bar = record(y=2, x=1)
foo == bar  # => true
Ken
quelle
1
Ordentlich (ich wusste, wie man das im Allgemeinen macht, wusste aber nicht, dass es so einfach ist). Nun zur eigentlichen Frage: Wären Sie bereit, einen PEP für das oben Gesagte einzureichen? :)
Pavel Minaev
2
Wird übrigens __neq__wirklich gebraucht? Wird die Standarddefinition nicht not __eq__automatisch bereitgestellt?
Pavel Minaev
1
Pavel: Das habe ich anfangs gedacht, aber als ich es ausprobiert habe, schien es nicht so zu funktionieren (obwohl es durchaus möglich ist, dass ich es vermasselt habe).
Ken
4
__neq__sollte sein __ne__, und nein, es wird nicht automatisch bereitgestellt.
Ethan Furman
4

Wenn Sie möchten, dass die Instanz ebenfalls anonym ist (indem Sie das Objekt direkt in einem Ausdruck verwenden), müssen Sie den Typausdruck verwenden. In vielen Fällen ist die Instanz jedoch nicht anonym, sondern einer Variablen zugeordnet. Dieser Fall kann in Python in angemessener Weise mit Metaklassen oder Dekoratoren behandelt werden.

Ein Beispiel mit Dekorateur:

def anonymous(cls):
    return cls()

@anonymous
class foo:
     x = 42

     def bar(self):
          return self.x

In diesem Fall bewirkt der Dekorator, dass die Klasse fooinstanziiert und fooanstelle der Klasse selbst in die Variable eingefügt wird. Auf die Klasse selbst kann von keinem Namespace aus zugegriffen werden, obwohl sie einen Namen hat:

>>> foo
<__main__.foo instance at 0x7fd2938d8320>
>>> foo.bar()
42

Ein weiteres Merkmal in Python, das für viele Anwendungsfälle geeignet ist, ist, dass es legal ist, Klassen lokal zu definieren, was bedeutet, dass sie zu einem lokalen Symbol für diese Funktion werden, was wiederum ein gewisses Maß an Anonymität verleiht.

Himmel König
quelle
4

Zitiert von dieser Seite :

 class Struct:
     def __init__(self, **entries): self.__dict__.update(entries)
     def __eq__(self, other): return self.__dict__ == other.__dict__
     def __ne__(self, other): return self.__dict__ != other.__dict__

 options = Struct(answer=42, linelen = 80, font='courier')
 options.answer
 >>> 42
 options.answer = 'plastics'
 vars(options)
 >>> {'answer': 'plastics', 'font': 'courier', 'linelen': 80}
Jean-François Fabre
quelle
es ist nicht neq es ist ne
Jean-François Fabre
0

Geben Sie hier die Bildbeschreibung ein

Wie oben gezeigt, bekomme ich dafür eine gute Intelligenz von pycharm und ich liebe diese Lösung ...

>>> def anonymous(cls):
...     return cls()
>>> class fooo:
...     @anonymous
...     class c:
...             D=10
>>> fooo.c.D
10
TooGeeky
quelle
@skyking mit Ihnen Hinweis und ich liebe dies ... Ich, die ich mehr Bestimmungen über Stackoverflow hatte
TooGeeky