Wie füge ich einer Klasse dynamisch Eigenschaften hinzu?

215

Ziel ist es, eine Scheinklasse zu erstellen, die sich wie eine Datenbank-Ergebnismenge verhält.

Wenn beispielsweise eine Datenbankabfrage mit einem Diktatausdruck zurückgegeben wird {'ab':100, 'cd':200}, möchte ich Folgendes sehen:

>>> dummy.ab
100

Zuerst dachte ich, ich könnte es vielleicht so machen:

ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
    def __init__(self, ks, vs):
        for i, k in enumerate(ks):
            self[k] = vs[i]
            setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))

    def fn_readonly(self, v)
        raise "It is ready only"

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

aber c.abGibt stattdessen ein Eigenschaftsobjekt zurück.

Ersetzen der setattrLeitung durchk = property(lambda x: vs[i]) ist überhaupt nicht sinnvoll.

Was ist also der richtige Weg, um zur Laufzeit eine Instanzeigenschaft zu erstellen?

PS Mir ist eine Alternative bekannt, die in Wie wird die __getattribute__Methode verwendet?

Anthony Kong
quelle
2
Ihr Code enthält einige Tippfehler: Die Definition von fn_readonly erfordert a :und __init__Verweise self.fn_readyonly.
Mhawke
Du hast recht. Ich habe diese Setter-Funktion in letzter Minute hinzugefügt, um den Grund für das Erstellen einer Eigenschaft zur Laufzeit zu unterstreichen.
Anthony Kong
Das Hauptproblem beim Erstellen von Eigenschaften bei der Initialisierung war, dass in einigen Fällen, wenn ich die Helfer kurz danach anrief oder ein Problem auftrat, die Fehlermeldung angezeigt wurde, dass sie trotz der Tatsache nicht vorhanden waren. In meiner folgenden Lösung erstelle ich 2 Klassen. Eine als Basis / Eltern (die ich zu vermeiden versuche) und das Hauptobjekt, das die Basis / Eltern erweitert. Dann rufe ich im Hauptobjekt ohne Initialisierung meinen AccessorFunc-Ersteller auf, der die Eigenschaften, Hilfsfunktionen und mehr erstellt.
Acecool
dh: Klasse ExampleBase: pass; Klasse Beispiel (ExampleBase): __x = Accessor (ExampleBase, 'x', 'X', 123); --- die eine Eigenschaft unter x und benannten Funktionen mit X erstellen würde, also GetX, SetX und mehr ... und .x, ._x und .__ x für die Eigenschaft. .X ist also die Eigenschaft selbst, durch die die Daten geleitet werden sollen (Abrufen / Einstellen über self.x = 123; oder self.x zum Ausgeben). Ich habe self._x für die gespeicherten RAW-Daten verwendet, damit sie leicht zugänglich sind, da ich auch die Zuweisung von Standardwerten zugelassen habe, ohne sie in den gespeicherten Daten festzulegen. _x könnte also None sein und .x könnte 123 zurückgeben. und .__ x mit dem Accessor
Acecool verbunden sein
Hier ist ein Link zur Basisversion, die dynamische Eigenschaften und dynamische Funktionen erstellt. Die Datei enthält eine Reihe von Links zu anderen Versionen. Eines ist das AccessorFunc-System, das eine Funktion zum Erstellen von Hilfsprogrammen verwendet (eines für Funktionen, eines für Eigenschaften, eines für beide als einzelne Elemente - es wird also keine Codeverkürzung in irgendetwas in dieser Datei verwendet). Wenn etwas fehlt, eines von Die anderen Dateien haben es: dropbox.com/s/phnnuavssmzeqrr/dynamic_properties_simple.py?dl=0
Acecool

Antworten:

333

Ich denke, ich sollte diese Antwort erweitern, jetzt wo ich älter und weiser bin und weiß, was los ist. Besser spät als nie.

Sie können einer Klasse dynamisch eine Eigenschaft hinzufügen. Aber das ist der Haken: Sie müssen es der Klasse hinzufügen .

>>> class Foo(object):
...     pass
... 
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4

A propertyist eigentlich eine einfache Implementierung einer Sache, die als Deskriptor bezeichnet wird . Es ist ein Objekt, das eine benutzerdefinierte Behandlung für ein bestimmtes Attribut für eine bestimmte Klasse bietet . Ein bisschen wie ein Weg, einen riesigen ifBaum herauszurechnen __getattribute__.

Als ich frage foo.bin dem obigen Beispiel sieht Python , dass die bdie auf der Klasse implementiert definiert Descriptor Protokoll -die nur bedeutet , es ist ein Objekt mit einem __get__, __set__oder __delete__Verfahren. Der Deskriptor übernimmt die Verantwortung für die Behandlung dieses Attributs, daher ruft Python auf Foo.b.__get__(foo, Foo), und der Rückgabewert wird als Wert des Attributs an Sie zurückgegeben. Im Fall von propertyruft jede dieser Methoden nur das fget, auf fset, oder fdelSie haben es an den propertyKonstruktor übergeben.

Deskriptoren sind wirklich Pythons Methode, um die Installation der gesamten OO-Implementierung aufzudecken. In der Tat gibt es eine andere Art von Deskriptor noch häufiger als property.

>>> class Foo(object):
...     def bar(self):
...         pass
... 
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>>
>>> Foo().bar.__get__
<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>

Die bescheidene Methode ist nur eine andere Art von Deskriptor. Seine __get__Tacks auf der aufrufenden Instanz als erstes Argument; in der Tat tut es dies:

def __get__(self, instance, owner):
    return functools.partial(self.function, instance)

Ich vermute jedenfalls, dass Deskriptoren deshalb nur für Klassen funktionieren: Sie sind eine Formalisierung der Dinge, die Klassen überhaupt antreiben. Sie sind sogar die Ausnahme von der Regel: Sie können einer Klasse offensichtlich Deskriptoren zuweisen, und Klassen sind selbst Instanzen von type! In der Tat versucht der Versuch, Foo.bnoch Anrufe zu lesen property.__get__; Es ist nur eine Redewendung für Deskriptoren, sich beim Zugriff als Klassenattribute zurückzugeben.

Ich finde es ziemlich cool, dass praktisch das gesamte OO-System von Python in Python ausgedrückt werden kann. :) :)

Oh, und ich habe vor einiger Zeit einen wortreichen Blog-Beitrag über Deskriptoren geschrieben, wenn Sie interessiert sind.

Eevee
quelle
35
Die add_property-Methode muss nicht hinzugefügt werden. setattr (Foo, 'name', property (func))
Courtney D
8
Ihr "Aber das ist der Haken ..." hat mir nur einige Stunden Arbeit erspart. Danke dir.
Matt Howell
2
Wenn Sie eine Eigenschaft für eine einzelne Instanz definieren möchten, können Sie zur Laufzeit eine Klasse erstellen und __class__ ändern .
Wilfred Hughes
1
Was ist mit @ myproperty.setter? Wie füge ich es dinamisch hinzu?
LRMAAX
Sie müssen einem initialisierten Objekt keine Eigenschaft hinzufügen. Dies kann bedeuten, dass es nur bei der Instanz bleibt, aber ich müsste das noch einmal überprüfen. Ich weiß, dass ich auf ein ähnliches Problem gestoßen bin, bei dem meine dynamischen Eigenschaften nur Instanzen waren. Am Ende hatte ich auch ein statisches Setup und das gewünschte, das sich auf das Objekt bezieht, damit zukünftige Initialisierungen sie verwenden würden. Mein Beitrag befindet sich unten und erstellt Hilfsfunktionen und einfache Möglichkeiten, auf alles zuzugreifen. .x für die Eigenschaft, ._x für die vom Getter / Setter verwendeten Rohdaten (die None sein können) und .__ x für das Accessor-Objekt.
Acecool
57

Ziel ist es, eine Scheinklasse zu erstellen, die sich wie eine Datenbank-Ergebnismenge verhält.

Was Sie also wollen, ist ein Wörterbuch, in dem Sie ein ['b'] als ab buchstabieren können?

Das ist einfach:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__
Bobince
quelle
1
In einem allgemeineren Setup dient dies einem begrenzten Zweck. Wenn das Diktat eine mehrstufige Hierarchie hat, wie d = {'a1': {'b': 'c'}, 'a2': ...}, dann können Sie, während Sie d.a1 oder d.a2 ausführen können, ' t do d.a1.b
Shreyas
1
Beachten Sie, dass auf diese Weise Attributwerte für Attribute mit demselben Namen wie diktierte Methoden oder Attribute festgelegt werden können, die Werte jedoch nicht erneut auf dieselbe Weise abgerufen werden können : d.items = 1, gibt d.itemszurück <built-in method items of atdict object at ...>. Sie könnten noch tun d["items"]oder verwenden __getattribute__statt __getattr__, aber dies verhindert , dass die meisten der dict Methoden verwenden.
Marcono1234
Verwenden Sie einfach die Munch- Bibliothek! (Gabel des Bündels)
Brian Peterson
38

Es scheint, dass Sie dieses Problem viel einfacher mit a lösen können namedtuple, da Sie die gesamte Liste der Felder im Voraus kennen.

from collections import namedtuple

Foo = namedtuple('Foo', ['bar', 'quux'])

foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux

foo2 = Foo()  # error

Wenn Sie unbedingt Ihren eigenen Setter schreiben müssen, müssen Sie die Metaprogrammierung auf Klassenebene durchführen. property()funktioniert nicht bei Instanzen.

Eevee
quelle
Großartige Idee. Leider bin ich im Moment mit Python 2.4 festgefahren.
Anthony Kong
3
Benanntes
Tupelrezept
2
Der Typ, der geschrieben hat, namedtupleverdient einen Preis dafür, dass er es glatt und elegant macht, treue objektorientierte Prinzipien zu sein.
Keith Pinson
4
Entschuldigung, bestenfalls gilt diese Antwort nur für den Sonderfall, in dem eine gewünschte Klasse nur aus schreibgeschützten Attributen besteht, die alle im Voraus kennen. Mit anderen Worten, ich denke nicht, dass es die umfassendere Frage behandelt, wie man einer Klasse zur Laufzeit allgemeine Eigenschaften hinzufügt - nicht nur schreibgeschützte - (und auch die aktuelle Version des anderen "Add-Ons" antwortet nicht geschrieben vom Autor).
Martineau
@martineau also ... mehr Argumente an übergeben property()? In beiden Antworten ist nichts spezifisch für schreibgeschützte Eigenschaften.
Eevee
32

Sie müssen dafür keine Eigenschaft verwenden. Überschreiben Sie __setattr__sie einfach , damit sie schreibgeschützt sind.

class C(object):
    def __init__(self, keys, values):
        for (key, value) in zip(keys, values):
            self.__dict__[key] = value

    def __setattr__(self, name, value):
        raise Exception("It is read only!")

Tada.

>>> c = C('abc', [1,2,3])
>>> c.a
1
>>> c.b
2
>>> c.c
3
>>> c.d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'd'
>>> c.d = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
>>> c.a = 'blah'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
Ryan
quelle
9

Wie füge ich einer Python-Klasse dynamisch Eigenschaften hinzu?

Angenommen, Sie haben ein Objekt, dem Sie eine Eigenschaft hinzufügen möchten. Normalerweise möchte ich Eigenschaften verwenden, wenn ich mit der Verwaltung des Zugriffs auf ein Attribut in Code beginnen möchte, der nachgelagert verwendet wird, damit ich eine konsistente API verwalten kann. Jetzt füge ich sie normalerweise dem Quellcode hinzu, in dem das Objekt definiert ist. Nehmen wir jedoch an, Sie haben diesen Zugriff nicht oder müssen Ihre Funktionen wirklich dynamisch programmgesteuert auswählen.

Erstellen Sie eine Klasse

Lassen Sie uns anhand eines Beispiels, das auf der Dokumentation fürproperty basiert, eine Objektklasse mit einem "versteckten" Attribut erstellen und eine Instanz davon erstellen:

class C(object):
    '''basic class'''
    _x = None

o = C()

In Python erwarten wir, dass es einen offensichtlichen Weg gibt, Dinge zu tun. In diesem Fall werde ich jedoch zwei Möglichkeiten zeigen: mit Dekorationsnotation und ohne. Erstens ohne Dekorationsnotation. Dies kann für die dynamische Zuweisung von Gettern, Setzern oder Löschern nützlicher sein.

Dynamisch (auch bekannt als Monkey Patching)

Lassen Sie uns einige für unsere Klasse erstellen:

def getx(self):
    return self._x

def setx(self, value):
    self._x = value

def delx(self):
    del self._x

Und jetzt ordnen wir diese der Eigenschaft zu. Beachten Sie, dass wir unsere Funktionen hier programmgesteuert auswählen und die dynamische Frage beantworten können:

C.x = property(getx, setx, delx, "I'm the 'x' property.")

Und Verwendung:

>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:

    I'm the 'x' property.

Dekorateure

Wir könnten das gleiche tun wie wir oben mit Dekorateur Notation taten, aber in diesem Fall, wir müssen die Methoden alle die gleichen Namen nennen (und ich würde empfehlen , es genauso aus wie das Attribut zu halten), so programmatische Zuordnung nicht so trivial ist , wie es verwendet die obige Methode:

@property
def x(self):
    '''I'm the 'x' property.'''
    return self._x

@x.setter
def x(self, value):
    self._x = value

@x.deleter
def x(self):
    del self._x

Und weisen Sie das Eigenschaftsobjekt mit seinen bereitgestellten Setzern und Löschern der Klasse zu:

C.x = x

Und Verwendung:

>>> help(C.x)
Help on property:

    I'm the 'x' property.

>>> o.x
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
Aaron Hall
quelle
5

Ich habe eine ähnliche Frage zu diesem Stapelüberlauf- Beitrag gestellt , um eine Klassenfactory zu erstellen, die einfache Typen erstellt. Das Ergebnis war diese Antwort, die eine funktionierende Version der Klassenfabrik hatte. Hier ist ein Ausschnitt der Antwort:

def Struct(*args, **kwargs):
    def init(self, *iargs, **ikwargs):
        for k,v in kwargs.items():
            setattr(self, k, v)
        for i in range(len(iargs)):
            setattr(self, args[i], iargs[i])
        for k,v in ikwargs.items():
            setattr(self, k, v)

    name = kwargs.pop("name", "MyStruct")
    kwargs.update(dict((k, None) for k in args))
    return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})

>>> Person = Struct('fname', 'age')
>>> person1 = Person('Kevin', 25)
>>> person2 = Person(age=42, fname='Terry')
>>> person1.age += 10
>>> person2.age -= 10
>>> person1.fname, person1.age, person2.fname, person2.age
('Kevin', 35, 'Terry', 32)
>>>

Sie können eine Variation davon verwenden, um Standardwerte zu erstellen, die Ihr Ziel sind (es gibt auch eine Antwort in dieser Frage, die sich damit befasst).

kjfletch
quelle
4

Ich bin mir nicht sicher, ob ich die Frage vollständig verstehe, aber Sie können die Instanzeigenschaften zur Laufzeit mit dem integrierten Element __dict__Ihrer Klasse ändern :

class C(object):
    def __init__(self, ks, vs):
        self.__dict__ = dict(zip(ks, vs))


if __name__ == "__main__":
    ks = ['ab', 'cd']
    vs = [12, 34]
    c = C(ks, vs)
    print(c.ab) # 12
Halbmond frisch
quelle
Im Wesentlichen ist meine Frage herauszufinden, ob es möglich ist, zur Laufzeit eine neue Eigenschaft zu erstellen. Der Konsens scheint negativ zu sein. Ihr Vorschlag ist sicherlich einfach und praktisch. (Gleiches gilt für andere Antworten, die dict verwenden )
Anthony Kong
Eine einfache Antwort wäre auch:self.__dict__[key] = value
Allan Karlson
4

Für diejenigen, die von Suchmaschinen kommen, sind hier die beiden Dinge, nach denen ich gesucht habe, wenn ich über dynamische Eigenschaften gesprochen habe:

class Foo:
    def __init__(self):
        # we can dynamically have access to the properties dict using __dict__
        self.__dict__['foo'] = 'bar'

assert Foo().foo == 'bar'


# or we can use __getattr__ and __setattr__ to execute code on set/get
class Bar:
    def __init__(self):
        self._data = {}
    def __getattr__(self, key):
        return self._data[key]
    def __setattr__(self, key, value):
        self._data[key] = value

bar = Bar()
bar.foo = 'bar'
assert bar.foo == 'bar'

__dict__ist gut, wenn Sie dynamisch erstellte Eigenschaften einfügen möchten. __getattr__Es ist gut, nur dann etwas zu tun, wenn der Wert benötigt wird, z. B. eine Datenbank abzufragen. Die Kombination set / get vereinfacht den Zugriff auf in der Klasse gespeicherte Daten (wie im obigen Beispiel).

Wenn Sie nur eine dynamische Eigenschaft möchten, sehen Sie sich die integrierte Funktion property () an.

tleb
quelle
4

Sie können property()einer Instanz zur Laufzeit keine neue hinzufügen , da Eigenschaften Datendeskriptoren sind. Stattdessen müssen Sie dynamisch eine neue Klasse erstellen oder überladen, __getattribute__um Datendeskriptoren für Instanzen zu verarbeiten.

Alex Gaynor
quelle
Das ist falsch. Sie können die Eigenschaft der Klasse hinzufügen und dann über die Methode darauf zugreifen.
Ahmed
2

Der beste Weg, dies zu erreichen, ist die Definition __slots__. Auf diese Weise können Ihre Instanzen keine neuen Attribute haben.

ks = ['ab', 'cd']
vs = [12, 34]

class C(dict):
    __slots__ = []
    def __init__(self, ks, vs): self.update(zip(ks, vs))
    def __getattr__(self, key): return self[key]

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

Das druckt 12

    c.ab = 33

Das gibt: AttributeError: 'C' object has no attribute 'ab'

nosklo
quelle
2

Nur ein weiteres Beispiel, wie der gewünschte Effekt erzielt werden kann

class Foo(object):

    _bar = None

    @property
    def bar(self):
        return self._bar

    @bar.setter
    def bar(self, value):
        self._bar = value

    def __init__(self, dyn_property_name):
        setattr(Foo, dyn_property_name, Foo.bar)

Jetzt können wir also Dinge tun wie:

>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5
Lehins
quelle
2

Hier ist eine Lösung, die:

  • Ermöglicht die Angabe von Eigenschaftsnamen als Zeichenfolgen , sodass diese von einer externen Datenquelle stammen können, anstatt dass alle in Ihrem Programm aufgeführt sind.
  • Fügt die Eigenschaften hinzu, wenn die Klasse definiert wird , anstatt jedes Mal, wenn ein Objekt erstellt wird.

Nachdem die Klasse definiert wurde, führen Sie dies einfach aus, um ihr dynamisch eine Eigenschaft hinzuzufügen:

setattr(SomeClass, 'propertyName', property(getter, setter))

Hier ist ein vollständiges Beispiel, das in Python 3 getestet wurde:

#!/usr/bin/env python3

class Foo():
  pass

def get_x(self):
  return 3

def set_x(self, value):
  print("set x on %s to %d" % (self, value))

setattr(Foo, 'x', property(get_x, set_x))

foo1 = Foo()
foo1.x = 12
print(foo1.x)
David Grayson
quelle
1

Mit dem folgenden Code können Sie Klassenattribute mithilfe eines Wörterbuchobjekts aktualisieren:

class ExampleClass():
    def __init__(self, argv):
        for key, val in argv.items():
            self.__dict__[key] = val

if __name__ == '__main__':
    argv = {'intro': 'Hello World!'}
    instance = ExampleClass(argv)
    print instance.intro
Anthony Holloman
quelle
1

Das ist ein bisschen anders als das, was OP wollte, aber ich habe mein Gehirn durcheinander gebracht, bis ich eine funktionierende Lösung gefunden habe, also setze ich mich hier für den nächsten Mann / die nächste Frau ein

Ich brauchte eine Möglichkeit, dynamische Setter und Getter anzugeben.

class X:
    def __init__(self, a=0, b=0, c=0):
        self.a = a
        self.b = b
        self.c = c

    @classmethod
    def _make_properties(cls, field_name, inc):
        _inc = inc

        def _get_properties(self):
            if not hasattr(self, '_%s_inc' % field_name):
                setattr(self, '_%s_inc' % field_name, _inc)
                inc = _inc
            else:
                inc = getattr(self, '_%s_inc' % field_name)

            return getattr(self, field_name) + inc

        def _set_properties(self, value):
            setattr(self, '_%s_inc' % field_name, value)

        return property(_get_properties, _set_properties)

Ich kenne meine Felder im Voraus, also werde ich meine Eigenschaften erstellen. HINWEIS: Sie können diese PER-Instanz nicht ausführen. Diese Eigenschaften sind für die Klasse vorhanden !!!

for inc, field in enumerate(['a', 'b', 'c']):
    setattr(X, '%s_summed' % field, X._make_properties(field, inc))

Lassen Sie uns jetzt alles testen.

x = X()
assert x.a == 0
assert x.b == 0
assert x.c == 0

assert x.a_summed == 0  # enumerate() set inc to 0 + 0 = 0
assert x.b_summed == 1  # enumerate() set inc to 1 + 0 = 1
assert x.c_summed == 2  # enumerate() set inc to 2 + 0 = 2

# we set the variables to something
x.a = 1
x.b = 2
x.c = 3

assert x.a_summed == 1  # enumerate() set inc to 0 + 1 = 1
assert x.b_summed == 3  # enumerate() set inc to 1 + 2 = 3
assert x.c_summed == 5  # enumerate() set inc to 2 + 3 = 5

# we're changing the inc now
x.a_summed = 1 
x.b_summed = 3 
x.c_summed = 5

assert x.a_summed == 2  # we set inc to 1 + the property was 1 = 2
assert x.b_summed == 5  # we set inc to 3 + the property was 2 = 5
assert x.c_summed == 8  # we set inc to 5 + the property was 3 = 8

Ist es verwirrend? Ja, tut mir leid, ich konnte keine aussagekräftigen Beispiele aus der Praxis finden. Auch dies ist nichts für Unbeschwerte.

Javier Buzzi
quelle
Wenn ich mich richtig erinnere, habe ich während all meiner Tests einen Weg gefunden, eine Eigenschaft vom Typ STATIC / dynamisch hinzugefügtes g / setter zu erstellen. Ich müsste alle meine vorherigen durchgehen - aber es ist definitiv möglich, etwas hinzuzufügen, das von allen Instanzen gemeinsam genutzt wird. Was das Erstellen eines Prozesses pro Instanz betrifft ... Ich bin mir ziemlich sicher, dass Sie dies können, damit eine Instanz etwas hat, was eine andere nicht hat. Ich müsste das überprüfen, aber ich bin auch auf so etwas gestoßen (bei meinen ersten Versuchen habe ich einen Fehler gemacht, der dazu führte, dass die Funktionen erstellt wurden, aber nicht alle Instanzen hatten sie aufgrund eines Fehlers)
Acecool
Auch jede mögliche Lösung ist willkommen, da dies ein Repo des Wissens ist. Es ist auch aufregend zu sehen, wie verschiedene Menschen Lösungen für ein Problem entwickeln. Meine Lösung macht eine Menge, Sie haben dies auf etwas reduziert, das einfacher zu teilen ist. Ich habe auch eine kleinere Variante von mir gemacht - es sollte irgendwo in diesem Thema sein - und ich habe gerade festgestellt, dass es nicht die ist, die ich gepostet habe: -) ...
Acecool
0

Dies scheint zu funktionieren (siehe aber unten):

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
        self.__dict__.update(self)
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

Wenn Sie ein komplexeres Verhalten benötigen, können Sie Ihre Antwort jederzeit bearbeiten.

bearbeiten

Folgendes wäre für große Datenmengen wahrscheinlich speichereffizienter:

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
    def __getattr__(self,name):
        return self[name]
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)
David X.
quelle
0

Um den Hauptschwerpunkt Ihrer Frage zu beantworten, möchten Sie ein schreibgeschütztes Attribut aus einem Diktat als unveränderliche Datenquelle:

Ziel ist es, eine Scheinklasse zu erstellen, die sich wie eine Datenbank-Ergebnismenge verhält.

Wenn zum Beispiel eine Datenbankabfrage mit einem Diktatausdruck zurückgegeben wird {'ab':100, 'cd':200}, würde ich sehen

>>> dummy.ab
100

Ich werde zeigen , wie eine verwenden , namedtupleaus dem collectionsModul zu erreichen , nur dies:

import collections

data = {'ab':100, 'cd':200}

def maketuple(d):
    '''given a dict, return a namedtuple'''
    Tup = collections.namedtuple('TupName', d.keys()) # iterkeys in Python2
    return Tup(**d)

dummy = maketuple(data)
dummy.ab

kehrt zurück 100

Aaron Hall
quelle
0
class atdict(dict):
  def __init__(self, value, **kwargs):
    super().__init__(**kwargs)
    self.__dict = value

  def __getattr__(self, name):
    for key in self.__dict:
      if type(self.__dict[key]) is list:
        for idx, item in enumerate(self.__dict[key]):
          if type(item) is dict:
            self.__dict[key][idx] = atdict(item)
      if type(self.__dict[key]) is dict:
        self.__dict[key] = atdict(self.__dict[key])
    return self.__dict[name]



d1 = atdict({'a' : {'b': [{'c': 1}, 2]}})

print(d1.a.b[0].c)

Und die Ausgabe ist:

>> 1
Serhii Khachko
quelle
0

Erweiterung der Idee von kjfletch

# This is my humble contribution, extending the idea to serialize
# data from and to tuples, comparison operations and allowing functions
# as default values.

def Struct(*args, **kwargs):
    FUNCTIONS = (types.BuiltinFunctionType, types.BuiltinMethodType, \
                 types.FunctionType, types.MethodType)
    def init(self, *iargs, **ikwargs):
        """Asume that unamed args are placed in the same order than
        astuple() yields (currently alphabetic order)
        """
        kw = list(self.__slots__)

        # set the unnamed args
        for i in range(len(iargs)):
            k = kw.pop(0)
            setattr(self, k, iargs[i])

        # set the named args
        for k, v in ikwargs.items():
            setattr(self, k, v)
            kw.remove(k)

        # set default values
        for k in kw:
            v = kwargs[k]
            if isinstance(v, FUNCTIONS):
                v = v()
            setattr(self, k, v)

    def astuple(self):
        return tuple([getattr(self, k) for k in self.__slots__])

    def __str__(self):
        data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__]
        return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data))

    def __repr__(self):
        return str(self)

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

    name = kwargs.pop("__name__", "MyStruct")
    slots = list(args)
    slots.extend(kwargs.keys())
    # set non-specific default values to None
    kwargs.update(dict((k, None) for k in args))

    return type(name, (object,), {
        '__init__': init,
        '__slots__': tuple(slots),
        'astuple': astuple,
        '__str__': __str__,
        '__repr__': __repr__,
        '__eq__': __eq__,
    })


Event = Struct('user', 'cmd', \
               'arg1', 'arg2',  \
               date=time.time, \
               __name__='Event')

aa = Event('pepe', 77)
print(aa)
raw = aa.astuple()

bb = Event(*raw)
print(bb)

if aa == bb:
    print('Are equals')

cc = Event(cmd='foo')
print(cc)

Ausgabe:

<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
Are equals
<Event: user=None, cmd=foo, arg1=None, arg2=None, date=1550051403.7938335>
asterio gonzalez
quelle
0

Obwohl viele Antworten gegeben werden, konnte ich keine finden, mit der ich zufrieden bin. Ich habe meine eigene Lösung gefunden, die propertyArbeit für den dynamischen Fall macht. Die Quelle zur Beantwortung der ursprünglichen Frage:

#!/usr/local/bin/python3

INITS = { 'ab': 100, 'cd': 200 }

class DP(dict):
  def __init__(self):
    super().__init__()
    for k,v in INITS.items():
        self[k] = v 

def _dict_set(dp, key, value):
  dp[key] = value

for item in INITS.keys():
  setattr(
    DP,
    item,
    lambda key: property(
      lambda self: self[key], lambda self, value: _dict_set(self, key, value)
    )(item)
  )

a = DP()
print(a)  # {'ab': 100, 'cd': 200}
a.ab = 'ab100'
a.cd = False
print(a.ab, a.cd) # ab100 False
Rob L.
quelle
0

Was für mich funktioniert, ist Folgendes:

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x

    x=property(g,s,d)


c = C()
c.x="a"
print(c.x)

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x)

Ausgabe

a
aa
Prosti
quelle
-1

Ich bin kürzlich auf ein ähnliches Problem gestoßen, die Lösung, die ich gefunden habe, __getattr__und __setattr__für die Eigenschaften, die ich behandeln möchte, wird alles andere an die Originale weitergegeben.

class C(object):
    def __init__(self, properties):
        self.existing = "Still Here"
        self.properties = properties

    def __getattr__(self, name):
        if "properties" in self.__dict__ and name in self.properties:
            return self.properties[name] # Or call a function, etc
        return self.__dict__[name]

    def __setattr__(self, name, value):
        if "properties" in self.__dict__ and name in self.properties:
            self.properties[name] = value
        else:
            self.__dict__[name] = value

if __name__ == "__main__":
    my_properties = {'a':1, 'b':2, 'c':3}
    c = C(my_properties)
    assert c.a == 1
    assert c.existing == "Still Here"
    c.b = 10
    assert c.properties['b'] == 10
teeks99
quelle
Ich habe dies untersucht, aber Sie gehen technisch eine Liste in Ihren Getter- und Setter-Helfern durch. Aus diesem Grund wäre jeder Anruf langsamer, da Sie ihn zuerst aus einer Liste nachschlagen, anstatt direkt darauf zuzugreifen. Es sei denn, Python ordnet es automatisch für Sie zu. Es mag sein, aber ich habe dies noch nicht bewertet, um es sicher zu wissen, aber es ist mir ein Anliegen, bevor ich es versuche. Zweitens müssen Sie auf diese Weise die Helfer anders definieren. Sie können Datentypen und / oder Werte auch nicht sperren, ohne ein großes Wörterbuch oder viele zusätzliche Zeilen zu erhalten.
Acecool
Das heißt: Ich müsste entweder eine Basisklasse erstellen, von der aus ich alle meine Kinder, von denen das System verwendet wird, erweitern kann, oder ich muss allen s / getattr-Zauberfunktionen hinzufügen und das System jedes Mal duplizieren. Die Deklaration von Eigenschaften bedeutet auch, dass Sie sie auf eine Weise einrichten müssen und wenn Sie zusätzliche Unterstützung wünschen, wie ich sie aufgelistet habe, wie z. B. Datentyp- und / oder Wertschutz, um die Zuweisung von Daten und andere Helfer zuzulassen oder zu verhindern Dann müssen Sie sie codieren. Zugegeben, Sie könnten das System im Verhalten ähnlich machen, aber es endet dort, wo Sie etwas anders und sperriger deklarieren.
Acecool
-1

Hier ist das einfache Beispiel zum programmgesteuerten Erstellen eines Eigenschaftsobjekts.

#!/usr/bin/python3

class Counter:
    def __init__(self):
        cls = self.__class__
        self._count = 0
        cls.count = self.count_ref()

    def count_get(self):
        print(f'count_get: {self._count}')
        return self._count

    def count_set(self, value):
        self._count = value
        print(f'count_set: {self._count}')

    def count_del(self):
        print(f'count_del: {self._count}')

    def count_ref(self):
        cls = self.__class__
        return property(fget=cls.count_get, fset=cls.count_set, fdel=cls.count_del)

counter = Counter()

counter.count
for i in range(5):
    counter.count = i
del counter.count

'''
output
======
count_get: 0
count_set: 0
count_set: 1
count_set: 2
count_set: 3
count_set: 4
count_del: 4
'''
Suhas Siddu
quelle
-2

Die einzige Möglichkeit, eine Eigenschaft dynamisch anzuhängen, besteht darin, eine neue Klasse und ihre Instanz mit Ihrer neuen Eigenschaft zu erstellen.

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)
M. Utku ALTINKAYA
quelle
1
Dies scheint nicht zu funktionieren. Es wird das Ergebnis der Eigenschaft zugewiesen, nicht die Eigenschaft selbst.
Montag,
Das ist falsch. Ich füge meinem System dynamische Eigenschaften hinzu, ohne die Klasse jemals initialisieren zu müssen. Initialisieren Sie x = Example () und fügen Sie dann die Eigenschaft zu x hinzu.
Acecool
Wenn Sie sich meinen Code ansehen, werden Sie sehen, dass ich die Klasse ExampleBase: pass und dann die Klasse Example (ExampleBase): ... verwende. Dann hänge ich die Eigenschaften an ExampleBase an, da der Name dann existiert und Example sich daraus erstreckt. es hat Zugang zu allem. Ich verwende die Variable __ var, damit der Accessor-Helfer direkten Zugriff auf die Accessor-Objekte hat. Ich verwende _ für die gespeicherten (rohen) Daten, die None sein können, und keine Unterstriche für die tatsächliche Eigenschaft, die den Getter durchläuft. Ich kann die Getter-Funktion mit einer dynamisch hinzugefügten Funktion aufrufen oder die Eigenschaft verwenden. Alles ohne vorher initiiert zu haben.
Acecool
Hinweis: Ich habe es erwähnt - aber meine Definition für Definition bedeutet, dass die Referenz im Namespace vorhanden ist - dh: class Beispiel (Objekt): pass ... Es existiert, wurde aber nicht initialisiert. Initialisierung bedeutet blah = Example (); Jetzt wurde das Objekt 'dupliziert' und konstruiert und dann als Referenz in bla gespeichert. --- Wenn Sie dies tun, sollten die dynamisch hinzugefügten Funktionen / Eigenschaften nur in der Instanz sein - das Problem, das ich damit hatte, war, selbst wenn die Funktionen vorhanden waren, gab es Fälle, in denen ich eine Fehlermeldung erhielt, dass dies nicht der Fall war. Entweder hat der Blockierungsfehler die Erstellung gestoppt oder die asynchrone Ausführung.
Acecool
Einige weitere Hinweise: Es ist möglich, Eigenschaften dynamisch zu erstellen und auf eine Weise zu funktionieren, die nur pro Instanz vorhanden ist. Sie können sie auch so gestalten, dass sie für das Objekt vorhanden sind (was in den meisten Fällen gewünscht wird). Und es gibt Fälle, in denen die hinzugefügten Elemente 'statisch' sind, dh dieselben Referenzen, und zurückgegebene Werte für alle Instanzen gemeinsam genutzt werden. Wenn Sie in einem Bereich aktualisieren, erhalten alle die gleichen
Werte
-6

Viele der bereitgestellten Antworten erfordern so viele Zeilen pro Eigenschaft, dh / und / oder - was ich als hässliche oder langwierige Implementierung betrachten würde, da für mehrere Eigenschaften Wiederholungen erforderlich sind usw. Ich ziehe es vor, die Dinge so lange zu reduzieren / zu vereinfachen, bis sie vorliegen kann nicht mehr vereinfacht werden oder bis es nicht mehr viel Sinn hat, dies zu tun.

Kurz gesagt: Wenn ich in abgeschlossenen Arbeiten zwei Codezeilen wiederhole, konvertiere ich sie normalerweise in eine einzeilige Hilfsfunktion usw. Ich vereinfache mathematische oder ungerade Argumente wie (start_x, start_y, end_x, end_y) in (x, y, w, h) dh x, y, x + w, y + h (manchmal ist min / max erforderlich oder wenn w / h negativ ist und die Implementierung es nicht mag, werde ich von x / subtrahieren y und abs w / h. etc ..).

Das Überschreiben der internen Getter / Setter ist ein guter Weg, aber das Problem ist, dass Sie dies für jede Klasse tun müssen oder die Klasse dieser Basis zuordnen müssen ... Dies funktioniert bei mir nicht so, wie ich es vorziehen würde frei zu wählen die Kinder / Eltern für Vererbung, Kinderknoten, etc.

Ich habe eine Lösung erstellt, die die Frage beantwortet, ohne einen Dict-Datentyp zur Bereitstellung der Daten zu verwenden, da ich die Eingabe der Daten usw. als mühsam empfinde.

Meine Lösung erfordert, dass Sie 2 zusätzliche Zeilen über Ihrer Klasse hinzufügen, um eine Basisklasse für die Klasse zu erstellen, zu der Sie die Eigenschaften hinzufügen möchten. Dann 1 Zeile pro Klasse. Sie haben die Möglichkeit, Rückrufe hinzuzufügen, um die Daten zu steuern und Sie zu informieren, wenn sich Daten ändern , beschränken Sie die Daten, die basierend auf Wert und / oder Datentyp festgelegt werden können, und vieles mehr.

Sie haben auch die Möglichkeit, _object.x, _object.x = value, _object.GetX (), _object.SetX (value) zu verwenden, und diese werden gleich behandelt.

Darüber hinaus sind die Werte die einzigen nicht statischen Daten, die der Klasseninstanz zugewiesen sind, aber die tatsächliche Eigenschaft wird der Klasse zugewiesen, dh die Dinge, die Sie nicht wiederholen möchten, müssen nicht wiederholt werden ... Sie kann einen Standardwert zuweisen, damit der Getter ihn nicht jedes Mal benötigt, obwohl es eine Option zum Überschreiben des Standardstandardwerts gibt, und es gibt eine andere Option, damit der Getter den gespeicherten Rohwert durch Überschreiben der Standardrückgaben zurückgibt (Hinweis: Diese Methode bedeutet, dass der Rohwert nur zugewiesen wird, wenn ein Wert zugewiesen wird, andernfalls ist er None - wenn der Wert Reset ist, weist er None usw. zu.)

Es gibt auch viele Hilfsfunktionen - die erste Eigenschaft, die hinzugefügt wird, fügt der Klasse ungefähr 2 Helfer hinzu, um auf die Instanzwerte zu verweisen ... Es handelt sich um ResetAccessors (_key, ..) -Varargs, die wiederholt werden (alle können mit den zuerst genannten Argumenten wiederholt werden ) und SetAccessors (_key, _value) mit der Option, der Hauptklasse mehr hinzuzufügen, um die Effizienz zu verbessern. Geplant sind: eine Möglichkeit, Accessoren zu gruppieren. Wenn Sie also dazu neigen, jedes Mal einige zurückzusetzen Sie können sie einer Gruppe zuweisen und die Gruppe zurücksetzen, anstatt die genannten Schlüssel jedes Mal zu wiederholen.

Der gespeicherte Instanz- / Rohwert wird in der Klasse gespeichert . , die Klasse. verweist auf die Accessor-Klasse, die statische Variablen / Werte / Funktionen für die Eigenschaft enthält. _Klasse. ist die Eigenschaft selbst, die beim Zugriff über die Instanzklasse beim Setzen / Abrufen usw. aufgerufen wird.

Die Accessor _class .__ zeigt auf die Klasse, aber da sie intern ist, muss sie in der Klasse zugewiesen werden, weshalb ich mich für die Verwendung von __Name = AccessorFunc (...) entschieden habe, eine einzelne Zeile pro Eigenschaft mit vielen optionalen zu verwendende Argumente (unter Verwendung von Keyed Varargs, da sie einfacher und effizienter zu identifizieren und zu warten sind) ...

Wie bereits erwähnt, erstelle ich auch viele Funktionen, von denen einige Accessor-Funktionsinformationen verwenden, sodass sie nicht aufgerufen werden müssen (da dies im Moment etwas unpraktisch ist - im Moment müssen Sie _class verwenden. .FunctionName (_class_instance) verwenden , args) - Ich bin herumgekommen, indem ich den Stack / Trace verwendet habe, um die Instanzreferenz abzurufen, um den Wert zu ermitteln, indem ich die Funktionen hinzugefügt habe, die entweder diesen Bitmarathon ausführen, oder indem ich die Accessoren zum Objekt hinzugefügt und self verwendet habe (benannte dies, um darauf hinzuweisen) sind für die Instanz und um den Zugriff auf sich selbst, die AccessorFunc-Klassenreferenz und andere Informationen aus den Funktionsdefinitionen zu behalten).

Es ist noch nicht ganz fertig, aber es ist ein fantastischer Halt. Hinweis: Wenn Sie zum Erstellen der Eigenschaften nicht __Name = AccessorFunc (...) verwenden, haben Sie keinen Zugriff auf die Taste __, obwohl ich sie in der Init-Funktion definiert habe. Wenn Sie dies tun, gibt es keine Probleme.

Außerdem: Beachten Sie, dass Name und Schlüssel unterschiedlich sind ... Der Name ist 'formal' und wird bei der Erstellung von Funktionsnamen verwendet. Der Schlüssel dient zur Datenspeicherung und zum Zugriff. dh _class.x wobei x in Kleinbuchstaben der Schlüssel ist, wäre der Name Großbuchstabe X, so dass GetX () die Funktion anstelle von Getx () ist, was etwas seltsam aussieht. Dadurch kann self.x funktionieren und angemessen aussehen, aber auch GetX () und angemessen aussehen.

Ich habe eine Beispielklasse eingerichtet, deren Schlüssel / Name identisch und unterschiedlich ist. Viele Hilfsfunktionen wurden erstellt, um die Daten auszugeben (Hinweis: Nicht alles ist vollständig), damit Sie sehen können, was los ist.

Die aktuelle Liste der Funktionen mit key: x, name: X gibt Folgendes aus:

Dies ist keineswegs eine umfassende Liste - es gibt einige, die es zum Zeitpunkt der Veröffentlichung noch nicht geschafft haben ...

_instance.SetAccessors( _key, _value [ , _key, _value ] .. )                   Instance Class Helper Function: Allows assigning many keys / values on a single line - useful for initial setup, or to minimize lines.    In short: Calls this.Set<Name>( _value ) for each _key / _value pairing.
_instance.ResetAccessors( _key [ , _key ] .. )                                 Instance Class Helper Function: Allows resetting many key stored values to None on a single line.                                           In short: Calls this.Reset<Name>() for each name provided.


Note: Functions below may list self.Get / Set / Name( _args ) - self is meant as the class instance reference in the cases below - coded as this in AccessorFuncBase Class.

this.GetX( _default_override = None, _ignore_defaults = False )                 GET:            Returns    IF ISSET: STORED_VALUE .. IF IGNORE_DEFAULTS: None  .. IF PROVIDED: DEFAULT_OVERRIDE ELSE: DEFAULT_VALUE       100
this.GetXRaw( )                                                                 RAW:            Returns    STORED_VALUE                                                                                                     100
this.IsXSet( )                                                                  ISSET:          Returns    ( STORED_VALUE != None )                                                                                         True

this.GetXToString( )                                                            GETSTR:         Returns    str( GET )                                                                                                       100
this.GetXLen( _default_override = None, _ignore_defaults = False )              LEN:            Returns    len( GET )                                                                                                       3
this.GetXLenToString( _default_override = None, _ignore_defaults = False )      LENSTR:         Returns    str( len( GET ) )                                                                                                3
this.GetXDefaultValue( )                                                        DEFAULT:        Returns    DEFAULT_VALUE                                                                                                    1111

this.GetXAccessor( )                                                            ACCESSOR:       Returns    ACCESSOR_REF ( self.__<key> )                                                                                    [ AccessorFuncBase ] Key: x : Class ID: 2231452344344 : self ID: 2231448283848        Default: 1111       Allowed Types: {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}     Allowed Values: None
this.GetXAllowedTypes( )                                                        ALLOWED_TYPES:  Returns    Allowed Data-Types                                                                                               {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}
this.GetXAllowedValues( )                                                       ALLOWED_VALUES: Returns    Allowed Values                                                                                                   None

this.GetXHelpers( )                                                             HELPERS:        Returns    Helper Functions String List - ie what you're reading now...                                                     THESE ROWS OF TEXT
this.GetXKeyOutput( )                                                           Returns information about this Name / Key                                                                                                   ROWS OF TEXT
this.GetXGetterOutput( )                                                        Returns information about this Name / Key                                                                                                   ROWS OF TEXT

this.SetX( _value )                                                             SET:            STORED_VALUE Setter - ie Redirect to __<Key>.Set                                                                            N / A
this.ResetX( )                                                                  RESET:          Resets STORED_VALUE to None                                                                                                 N / A

this.HasXGetterPrefix( )                                                        Returns Whether or Not this key has a Getter Prefix...                                                                                      True
this.GetXGetterPrefix( )                                                        Returns Getter Prefix...                                                                                                                    Get

this.GetXName( )                                                                Returns Accessor Name - Typically Formal / Title-Case                                                                                       X
this.GetXKey( )                                                                 Returns Accessor Property Key - Typically Lower-Case                                                                                        x
this.GetXAccessorKey( )                                                         Returns Accessor Key - This is to access internal functions, and static data...                                                             __x
this.GetXDataKey( )                                                             Returns Accessor Data-Storage Key - This is the location where the class instance value is stored..                                         _x

Einige der ausgegebenen Daten sind:

Dies ist für eine brandneue Klasse, die mit der Demo-Klasse erstellt wurde, ohne dass andere Daten als der Name zugewiesen wurden (damit sie ausgegeben werden können). Dies ist _foo, der Variablenname, den ich verwendet habe ...

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        1111                | _x:       None                     | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        2222                | _y:       None                     | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        3333                | _z:       None                     | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     <class 'int'>       | _Blah:    None                     | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    1                   | _Width:   None                     | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   0                   | _Height:  None                     | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    2                   | _Depth:   None                     | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |


this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       False     this.GetX( ):        1111                     this.GetXRaw( ):       None                     this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       4    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       False     this.GetY( ):        2222                     this.GetYRaw( ):       None                     this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       4    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       False     this.GetZ( ):        3333                     this.GetZRaw( ):       None                     this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       4    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    False     this.GetBlah( ):     <class 'int'>            this.GetBlahRaw( ):    None                     this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    13   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   False     this.GetWidth( ):    1                        this.GetWidthRaw( ):   None                     this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   1    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   False     this.GetDepth( ):    2                        this.GetDepthRaw( ):   None                     this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  False     this.GetHeight( ):   0                        this.GetHeightRaw( ):  None                     this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

Und dies ist, nachdem alle _foo-Eigenschaften (außer dem Namen) die folgenden Werte in derselben Reihenfolge zugewiesen haben: 'string', 1.0, True, 9, 10, False

this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       True      this.GetX( ):        10                       this.GetXRaw( ):       10                       this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       2    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       True      this.GetY( ):        10                       this.GetYRaw( ):       10                       this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       2    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       True      this.GetZ( ):        10                       this.GetZRaw( ):       10                       this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       2    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    True      this.GetBlah( ):     string Blah              this.GetBlahRaw( ):    string Blah              this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    11   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   True      this.GetWidth( ):    False                    this.GetWidthRaw( ):   False                    this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   5    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   True      this.GetDepth( ):    9                        this.GetDepthRaw( ):   9                        this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  True      this.GetHeight( ):   9                        this.GetHeightRaw( ):  9                        this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        10                  | _x:       10                       | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        10                  | _y:       10                       | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        10                  | _z:       10                       | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     string Blah         | _Blah:    string Blah              | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    False               | _Width:   False                    | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   9                   | _Height:  9                        | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    9                   | _Depth:   9                        | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |

Beachten Sie, dass aufgrund eingeschränkter Datentypen oder Werteinschränkungen einige Daten nicht zugewiesen wurden - dies ist beabsichtigt. Der Setter verhindert, dass fehlerhafte Datentypen oder Werte zugewiesen werden, auch wenn sie nicht als Standardwert zugewiesen werden (es sei denn, Sie überschreiben das Standardwertschutzverhalten).

Der Code wurde hier nicht veröffentlicht, weil ich nach den Beispielen und Erklärungen keinen Platz hatte ... Auch weil er sich ändern wird.

Bitte beachten Sie: Zum Zeitpunkt dieses Beitrags ist die Datei unordentlich - dies wird sich ändern. Wenn Sie es jedoch in Sublime Text ausführen und kompilieren oder in Python ausführen, werden eine Menge Informationen kompiliert und ausgespuckt - der AccessorDB-Teil wird nicht ausgeführt (der zum Aktualisieren des Print Getters- und GetKeyOutput-Hilfsprogramms verwendet wird Funktionen zusammen mit der Änderung in eine Instanzfunktion, wahrscheinlich in eine einzelne Funktion eingefügt und umbenannt - suchen Sie danach ..)

Weiter: Es ist nicht alles erforderlich, damit es ausgeführt werden kann. Viele der kommentierten Elemente unten enthalten weitere Informationen zum Debuggen. Möglicherweise ist es beim Herunterladen nicht vorhanden. Wenn dies der Fall ist, sollten Sie in der Lage sein, Kommentare abzugeben und neu zu kompilieren, um weitere Informationen zu erhalten.

Ich suche nach einer Lösung, um MyClassBase zu benötigen: pass, MyClass (MyClassBase): ... - wenn Sie eine Lösung kennen - posten Sie sie.

Das einzige, was in der Klasse benötigt wird, sind die __-Zeilen - der str dient zum Debuggen, ebenso wie der init - sie können aus der Demo-Klasse entfernt werden, aber Sie müssen einige der folgenden Zeilen auskommentieren oder entfernen (_foo / 2/3 ) ..

Die Klassen String, Dict und Util oben sind Teil meiner Python-Bibliothek - sie sind nicht vollständig. Ich habe ein paar Dinge, die ich brauchte, aus der Bibliothek kopiert und ein paar neue erstellt. Der vollständige Code wird mit der gesamten Bibliothek verknüpft und enthält diese zusammen mit der Bereitstellung aktualisierter Aufrufe und dem Entfernen des Codes (der einzige verbleibende Code ist die Demo-Klasse und die Druckanweisungen - das AccessorFunc-System wird in die Bibliothek verschoben). ..

Teil der Datei:

##
## MyClass Test AccessorFunc Implementation for Dynamic 1-line Parameters
##
class AccessorFuncDemoClassBase( ):
    pass
class AccessorFuncDemoClass( AccessorFuncDemoClassBase ):
    __Name      = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Name',      default = 'AccessorFuncDemoClass',  allowed_types = ( TYPE_STRING ),                    allowed_values = VALUE_ANY,                 documentation = 'Name Docs',        getter_prefix = 'Get',  key = 'Name',       allow_erroneous_default = False,    options = { } )
    __x         = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'X',         default = 1111,                     allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ),       allowed_values = VALUE_ANY,                 documentation = 'X Docs',           getter_prefix = 'Get',  key = 'x',          allow_erroneous_default = False,    options = { } )
    __Height    = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Height',    default = 0,                        allowed_types = TYPE_INTEGER,                       allowed_values = VALUE_SINGLE_DIGITS,       documentation = 'Height Docs',      getter_prefix = 'Get',  key = 'Height',     allow_erroneous_default = False,    options = { } )

Diese Schönheit macht es unglaublich einfach, neue Klassen mit dynamisch hinzugefügten Eigenschaften mit AccessorFuncs / Rückrufen / Datentyp / Wertdurchsetzung usw. zu erstellen.

Derzeit befindet sich der Link unter (Dieser Link sollte Änderungen am Dokument widerspiegeln.): Https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0

Außerdem: Wenn Sie Sublime Text nicht verwenden, empfehle ich ihn gegenüber Notepad ++, Atom, Visual Code und anderen, da die Verwendung von Threading-Implementierungen die Verwendung erheblich beschleunigt. Ich arbeite auch an einem IDE-ähnlichen Code Mapping-System dafür - werfen Sie einen Blick auf: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (Repo zuerst im Paket-Manager hinzufügen, dann Plugin installieren - wenn Version 1.0.0 fertig ist, werde ich hinzufügen es zur Haupt-Plugin-Liste ...)

Ich hoffe diese Lösung hilft ... und wie immer:

Nur weil es funktioniert, macht es nicht richtig - Josh 'Acecool' Moser

Acecool
quelle
Ich wollte eine schnelle Anzeige hinzufügen, wie die Klasse aussah, damit Sie die
Codedatei
Anscheinend bekommt dies viel Hass, was verwirrend ist. Es macht genau das, was das OP verlangt - dynamisches Hinzufügen von Eigenschaften zu einem Objekt. Es werden auch Hilfsfunktionen hinzugefügt, die nicht enthalten sein müssen - vielleicht wird es deshalb zu Hass - und es stellt sicher, dass der Entwickler auf einfache Weise auf die Eigenschaft (.x) zugreifen kann, die über den Getter, das, verarbeitet wird Der gespeicherte Rohwert (._x) kann None sein, wenn .x den Standardwert oder etwas anderes zurückgibt, und eine Möglichkeit, auf den Accessor zuzugreifen, um Helfer zu verwenden, Dinge zu ändern usw. (.__ x) ....
Acecool