Come aggiungere dinamicamente una proprietà a una classe?


234

L'obiettivo è creare una classe fittizia che si comporti come un gruppo di risultati db.

Quindi, ad esempio, se una query di database restituisce, utilizzando un'espressione dict {'ab':100, 'cd':200}, allora vorrei vedere:

>>> dummy.ab
100

All'inizio ho pensato che forse avrei potuto farlo in questo modo:

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

ma c.abrestituisce invece un oggetto proprietà.

Sostituire il setattrfilo con k = property(lambda x: vs[i])non serve a niente.

Allora qual è il modo giusto per creare una proprietà di istanza in fase di esecuzione?

PS Sono a conoscenza di un'alternativa presentata in Come viene __getattribute__utilizzato il metodo?


2
Ci sono alcuni errori di battitura nel codice: la definizione di fn_readonly necessita di a :e __init__riferimenti self.fn_readyonly.
mhawke

Hai ragione. Ho aggiunto quella funzione setter all'ultimo minuto per sottolineare il motivo della creazione di una proprietà in runtime.
Anthony Kong

Il problema principale che ho riscontrato con la creazione di proprietà durante l'inizializzazione era che, in alcuni casi, se chiamavo gli helper rapidamente dopo, o si verificava un problema, avrei ricevuto un errore che non esistevano nonostante il fatto che lo fossero. Nella mia soluzione di seguito, creo 2 classi. Uno come Base / Parent (che sto cercando di trovare una soluzione per evitare) e l'oggetto principale, che estende la Base / Parent. Quindi, nell'oggetto principale, senza inizializzare, chiamo il mio creatore di AccessorFunc che crea le proprietà, le funzioni di supporto e altro.
Acecool

cioè: classe ExampleBase: pass; class Esempio (ExampleBase): __x = Accessor (ExampleBase, 'x', 'X', 123); --- che creerebbe una proprietà sotto x e le funzioni denominate usando X, quindi GetX, SetX e altro ... e .x, ._x e .__ x per la proprietà. Quindi .x è la proprietà stessa per il passaggio dei dati (ottenere / impostare tramite self.x = 123; o self.x per l'output). Ho usato self._x per i dati RAW memorizzati in modo che potesse essere facilmente accessibile poiché ho anche consentito l'assegnazione di valori predefiniti, senza impostarli nei dati memorizzati. quindi _x potrebbe essere Nessuno e .x potrebbe restituire 123. e .__ x collegati ad Accessor
Acecool

Ecco un collegamento alla versione di base che crea proprietà dinamiche e funzioni dinamiche: il file ha una serie di collegamenti ad altre versioni. Uno è il sistema AccessorFunc che utilizza una funzione per creare helper (uno per le funzioni, uno per le proprietà, uno per entrambi come singoli elementi, quindi non utilizza l'accorciamento del codice in nulla in quel file) .. Se manca qualcosa, uno dei gli altri file ce l'hanno
Acecool

Risposte:


345

Suppongo che dovrei espandere questa risposta, ora che sono più vecchio e più saggio e so cosa sta succedendo. Meglio tardi che mai.

È possibile aggiungere dinamicamente una proprietà a una classe. Ma questo è il trucco: devi aggiungerlo alla classe .

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

A propertyè in realtà una semplice implementazione di una cosa chiamata descrittore . È un oggetto che fornisce una gestione personalizzata per un dato attributo, su una data classe . Un po 'come un modo per scomporre un enorme ifalbero __getattribute__.

Quando chiedo foo.bnell'esempio di cui sopra, Python vede che il bdefinito gli strumenti di classe il protocollo descrittore -che solo significa che è un oggetto con una __get__, __set__o __delete__metodo. Il descrittore rivendica la responsabilità della gestione di quell'attributo, quindi Python chiama Foo.b.__get__(foo, Foo)e il valore restituito ti viene passato come valore dell'attributo. Nel caso di property, ciascuno di questi metodi chiama solo il fget, fseto fdelsi passati al propertycostruttore.

I descrittori sono in realtà il modo in cui Python espone l'idoneità dell'intera implementazione OO. In effetti, esiste un altro tipo di descrittore ancora più comune di 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>

Il metodo umile è solo un altro tipo di descrittore. Il suo punto __get__sull'istanza chiamante come primo argomento; in effetti, fa questo:

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

Ad ogni modo, sospetto che questo sia il motivo per cui i descrittori funzionano solo sulle classi: sono una formalizzazione di ciò che alimenta le classi in primo luogo. Sono persino l'eccezione alla regola: puoi ovviamente assegnare descrittori a una classe e le classi sono esse stesse istanze di type! In effetti, provare a leggere Foo.bancora chiama property.__get__; è semplicemente idiomatico che i descrittori restituiscano se stessi quando si accede come attributi di classe.

Penso che sia abbastanza interessante che praticamente tutto il sistema OO di Python possa essere espresso in Python. :)

Oh, e ho scritto un post prolisso sul blog sui descrittori qualche tempo fa, se sei interessato.


39
Non è necessario aggiungere il metodo add_property. setattr (Foo, 'name', property (func))
Courtney D

8
Il tuo "Ma questo è il trucco ..." mi ha appena risparmiato diverse ore di lavoro. Grazie.
Matt Howell

4
Se desideri definire una proprietà su una singola istanza, puoi creare una classe in fase di esecuzione e modificare __class__ .
Wilfred Hughes

1
che dire di @ myproperty.setter? Come aggiungerlo dinamicamente?
LRMAAX

Non è necessario aggiungere una proprietà a un oggetto inizializzato. Ciò potrebbe significare che si attacca solo all'istanza, ma dovrei ricontrollarlo. So di essermi imbattuto in un problema simile in cui le mie proprietà dinamiche erano solo istanza, ho anche finito con una configurazione statica e quella che volevo che è per l'oggetto in modo che le inizializzazioni future ne facciano uso. Il mio post è di seguito e crea funzioni di supporto e modi semplici per accedere facilmente a tutto. .x per la proprietà, ._x per i dati grezzi utilizzati dal getter / setter (che può essere Nessuno) e .__ x per l'oggetto accessor.
Acecool

60

L'obiettivo è creare una classe fittizia che si comporti come un gruppo di risultati db.

Quindi quello che vuoi è un dizionario in cui puoi scrivere a ['b'] come ab?

Questo è facile:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

1
in una configurazione più generale, questo ha uno scopo limitato. se il dict ha una gerarchia multilivello, come d = {'a1': {'b': 'c'}, 'a2': ...}, allora mentre puoi fare d.a1 o d.a2, puoi ' t do d.a1.b
Shreyas

1
Una cosa da tenere a mente è che questo consente di impostare i valori di attributo per gli attributi con lo stesso nome di metodi dict o attributi, ma non consente il recupero allo stesso modo nuovo i valori: d.items = 1, d.itemsi rendimenti <built-in method items of atdict object at ...>. Puoi ancora fare d["items"]o usare __getattribute__invece di __getattr__, ma questo impedisce di usare la maggior parte dei metodi di dict.
Marcono1234

Usa la libreria di munch ! (forchetta di mazzo)
Brian Peterson

38

Sembra che tu possa risolvere questo problema molto più semplicemente con a namedtuple, poiché conosci l'intero elenco di campi in anticipo.

from collections import namedtuple

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

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

foo2 = Foo()  # error

Se hai assolutamente bisogno di scrivere il tuo setter, dovrai fare la metaprogrammazione a livello di classe; property()non funziona sulle istanze.


Grande idea. Sfortunatamente al momento sono bloccato con Python 2.4.
Anthony Kong


3
Il ragazzo che ha scritto namedtuplemerita un premio per aver reso fluido ed elegante essere fedeli ai principi orientati agli oggetti.
Keith Pinson,

4
Spiacente, nella migliore delle ipotesi, questa risposta è applicabile solo al caso speciale in cui si desidera una classe composta solo da attributi di sola lettura che tutti conoscono in anticipo. In altre parole non penso che affronti la domanda più ampia di come aggiungere proprietà generali - non solo di sola lettura - a una classe in fase di esecuzione (né la versione corrente dell'altro "componente aggiuntivo" risponde anche pubblicato dall'autore).
martineau

@martineau quindi ... passare altri argomenti a property()? non c'è niente in nessuna delle risposte che sia specifico per le proprietà di sola lettura.
Eevee

35

Non è necessario utilizzare una proprietà per questo. Esegui l'override __setattr__per renderli di sola lettura.

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!

9

Come aggiungere dinamicamente una proprietà a una classe Python?

Supponi di avere un oggetto a cui desideri aggiungere una proprietà. In genere, desidero utilizzare le proprietà quando devo iniziare a gestire l'accesso a un attributo nel codice che ha un utilizzo a valle, in modo da poter mantenere un'API coerente. Ora li aggiungerò tipicamente al codice sorgente in cui è definito l'oggetto, ma supponiamo che tu non abbia quell'accesso o che tu debba scegliere veramente dinamicamente le tue funzioni a livello di codice.

Crea un corso

Usando un esempio basato sulla documentazione perproperty , creiamo una classe di oggetto con un attributo "nascosto" e creiamo un'istanza di esso:

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

o = C()

In Python, ci aspettiamo che ci sia un modo ovvio di fare le cose. Tuttavia, in questo caso, mostrerò due modi: con la notazione del decoratore e senza. Primo, senza la notazione del decoratore. Questo può essere più utile per l'assegnazione dinamica di getter, setter o deleters.

Dinamico (aka Monkey Patching)

Creiamo alcuni per la nostra classe:

def getx(self):
    return self._x

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

def delx(self):
    del self._x

E ora li assegniamo alla proprietà. Nota che potremmo scegliere le nostre funzioni in modo programmatico qui, rispondendo alla domanda dinamica:

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

E utilizzo:

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

    I'm the 'x' property.

Decoratori

Si potrebbe fare lo stesso come abbiamo fatto in precedenza con la notazione decoratore, ma in questo caso, si deve nominare i metodi di tutte lo stesso nome (e lo consiglierei mantenendo lo stesso come attributo), quindi l'assegnazione programmatica non è così banale come sta usando il metodo sopra:

@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

E assegna alla classe l'oggetto proprietà con i suoi setter e deleter forniti:

C.x = x

E utilizzo:

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

5

Non sono sicuro di aver compreso completamente la domanda, ma puoi modificare le proprietà dell'istanza in fase di esecuzione con il built-in __dict__della tua classe:

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

In sostanza la mia domanda è scoprire se è possibile creare una nuova proprietà in runtime. Il consenso sembra essere negativo. Il tuo suggerimento è sicuramente semplice e pratico. (Lo stesso per altre risposte che usano dict )
Anthony Kong

1
Una semplice risposta potrebbe essere anche:self.__dict__[key] = value
Allan Karlson

5

Ho posto una domanda simile in questo post di Stack Overflow per creare una fabbrica di classi che ha creato tipi semplici. Il risultato fu questa risposta che aveva una versione funzionante della fabbrica di classi. Ecco uno snippet della risposta:

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

Potresti usare qualche variazione di questo per creare valori predefiniti che è il tuo obiettivo (c'è anche una risposta in quella domanda che tratta di questo).


5

Per chi proviene dai motori di ricerca, ecco le due cose che stavo cercando quando parlavo di proprietà dinamiche :

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__è utile se vuoi inserire proprietà create dinamicamente. __getattr__è utile fare qualcosa solo quando il valore è necessario, come interrogare un database. La combinazione set / get è utile per semplificare l'accesso ai dati memorizzati nella classe (come nell'esempio sopra).

Se vuoi solo una proprietà dinamica, dai un'occhiata alla funzione incorporata property () .


5

Ecco una soluzione che:

  • Consente di specificare i nomi delle proprietà come stringhe , in modo che possano provenire da un'origine dati esterna invece di essere tutti elencati nel programma.
  • Aggiunge le proprietà quando viene definita la classe , invece che ogni volta che viene creato un oggetto.

Dopo che la classe è stata definita, devi solo farlo per aggiungere una proprietà dinamicamente:

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

Ecco un esempio completo, testato in Python 3:

#!/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)

4

Non è possibile aggiungere un nuovo property()a un'istanza in fase di esecuzione, perché le proprietà sono descrittori di dati. È invece necessario creare dinamicamente una nuova classe o __getattribute__eseguire l' overload per elaborare i descrittori di dati sulle istanze.


Questo è sbagliato. È possibile aggiungere la proprietà alla classe e quindi accedervi dal metodo.
Ahmed

4

È possibile utilizzare il codice seguente per aggiornare gli attributi della classe utilizzando un oggetto dizionario:

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

Questo è semplice, elegante, diretto ed è esattamente quello che stavo cercando. Grazie mille!
Austin Dean

Il mio preferito per quello che mi serviva, facile e preciso.
NealWalters

2

Il modo migliore per ottenere risultati è definire __slots__. In questo modo le tue istanze non possono avere nuovi attributi.

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

Quello stampa 12

    c.ab = 33

Ciò dà: AttributeError: 'C' object has no attribute 'ab'


2

Solo un altro esempio di come ottenere l'effetto desiderato

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)

Quindi ora possiamo fare cose come:

>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5

2

Questo è un po 'diverso da quello che voleva OP, ma ho scosso il cervello fino a quando non ho trovato una soluzione funzionante, quindi lo metto qui per il prossimo ragazzo / ragazza

Avevo bisogno di un modo per specificare setter e getter dinamici.

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)

Conosco i miei campi in anticipo, quindi creerò le mie proprietà. NOTA: non puoi farlo PER istanza, queste proprietà esisteranno nella classe !!!

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

Proviamo tutto adesso ..

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

È confuso? Sì, mi dispiace di non essere riuscito a trovare esempi significativi del mondo reale. Inoltre, questo non è per i leggeri.


Se ricordo bene, ho trovato un modo durante tutti i miei test per creare una proprietà di tipo STATICO / g / setter aggiunto dinamicamente. Dovrei passare attraverso tutto il mio precedente, ma essere in grado di aggiungere qualcosa che è condiviso tra tutte le istanze è sicuramente possibile. Per quanto riguarda la creazione di un processo per istanza ... sono abbastanza sicuro che tu possa in modo che un'istanza abbia qualcosa che un'altra non ha. Avrei dovuto verificare, ma mi sono imbattuto anche in qualcosa del genere (nei miei primi tentativi ho commesso un errore che ha causato la creazione delle funzioni, ma non tutte le istanze le avevano a causa di un difetto)
Acecool

Inoltre, ogni possibile soluzione è benvenuta in quanto si tratta di un repo di conoscenza. È anche emozionante vedere i diversi modi in cui persone diverse creano soluzioni a un problema. La mia soluzione fa MOLTO, hai ridotto questo a qualcosa di più semplice da condividere. Ho fatto anche una mia variante più piccola - dovrebbe essere da qualche parte in questo argomento - e mi sono appena reso conto che non è quella che ho postato: -) ...
Acecool

1

Sebbene vengano fornite molte risposte, non sono riuscito a trovarne una di cui sono soddisfatto. Ho trovato la mia soluzione che fa propertyfunzionare il caso dinamico. La fonte per rispondere alla domanda originale:

#!/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

0

Sembra funzionare (ma vedi sotto):

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

Se hai bisogno di un comportamento più complesso, sentiti libero di modificare la tua risposta.

modificare

Quanto segue sarebbe probabilmente più efficiente in termini di memoria per set di dati di grandi dimensioni:

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

0

Per rispondere all'essenziale della tua domanda, vuoi un attributo di sola lettura da un dict come origine dati immutabile:

L'obiettivo è creare una classe fittizia che si comporti come un gruppo di risultati db.

Quindi, ad esempio, se una query di database restituisce, utilizzando un'espressione dict {'ab':100, 'cd':200}, allora vorrei vedere

>>> dummy.ab
100

Dimostrerò come utilizzare un namedtupledal collectionsmodulo per ottenere proprio questo:

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

ritorna 100


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)

E l'output è:

>> 1

0

Estendendo l'idea da 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)

Produzione:

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

0

Qualcosa che funziona per me è questo:

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)

Produzione

a
aa

-1

Recentemente mi sono imbattuto in un problema simile, la soluzione che ho escogitato utilizza __getattr__e __setattr__per le proprietà che voglio che gestisca, tutto il resto viene trasmesso agli originali.

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

Ho esaminato questo, tuttavia tecnicamente stai esaminando un elenco dei tuoi aiutanti getter e setter. Per questo motivo, ogni chiamata sarebbe più lenta perché la stai cercando da un elenco, prima, invece di accedervi direttamente. A meno che Python non lo mappi automaticamente per te; potrebbe, ma non l'ho ancora valutato per saperlo con certezza, ma è una preoccupazione per me prima di provarlo. In secondo luogo, in questo modo, devi definire gli helper in un altro modo. Inoltre, non puoi bloccare tipi di dati e / o valori senza finire con un dizionario di grandi dimensioni o molte righe extra.
Acecool

cioè: dovrei creare una classe base da cui estendo tutti i miei figli che usano il sistema, OPPURE devo aggiungere s / getattr funzioni magiche a tutto e duplicare il sistema ogni volta. La dichiarazione delle proprietà significa anche che devi configurarle in un modo e se desideri qualsiasi supporto aggiuntivo, come ho elencato come il tipo di dati e / o la protezione del valore per consentire o impedire l'assegnazione dei dati e altri helper , quindi devi codificarli. Certo, potresti rendere il sistema simile nel comportamento ma finisce dove dichiari in modo leggermente diverso e più ingombrante.
Acecool

-1

Ecco il semplice esempio per creare un oggetto proprietà a livello di codice.

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

-2

L'unico modo per collegare dinamicamente una proprietà è creare una nuova classe e la sua istanza con la nuova proprietà.

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)

1
questo non sembra funzionare. assegnerà il risultato della proprietà, non la proprietà stessa.
lunedì

Questo non è corretto. Allego proprietà dinamiche al mio sistema senza dover mai inizializzare la classe. Inizializza essendo x = Example (), quindi aggiungendo la proprietà a x.
Acecool

Se guardi il mio codice, vedrai che uso la classe ExampleBase: pass, quindi la classe Example (ExampleBase): ... quindi allego le proprietà a ExampleBase, perché il nome allora esiste, e poiché Example si estende da esso, ha accesso a tutto. Uso __ var per l'helper accessor per poter avere accesso diretto agli oggetti accessor, uso _ per i dati memorizzati (raw) che può essere None e nessun trattino basso per la proprietà effettiva che passa attraverso il getter. Posso chiamare la funzione getter utilizzando una funzione aggiunta dinamicamente o utilizzare la proprietà. Tutto senza aver prima iniziato.
Acecool

Nota: l'ho menzionato - ma la mia definizione di definizione significa che il riferimento esiste nello spazio dei nomi - ad esempio: class Example (Object): pass ... Esiste, ma non è stato inizializzato. L'inizializzazione significa blah = Example (); ora l'oggetto è stato "duplicato" e costruito, quindi memorizzato come riferimento in blah. --- Se lo fai, le funzioni / proprietà aggiunte dinamicamente dovrebbero essere solo nell'istanza - il problema che ho avuto con questo era anche se le funzioni esistevano, ci sono stati casi in cui ho ricevuto un errore che diceva che non lo facevano. L'errore di blocco ha interrotto la creazione o l'esecuzione asincrona.
Acecool

Alcune altre note: è possibile creare proprietà dinamicamente e funzioni in un modo che esistono solo per istanza. Puoi anche crearli in modo che esistano per l'oggetto (che è quello che vorresti nella maggior parte dei casi). E ci sono casi in cui gli elementi aggiunti sono 'statici', cioè gli stessi riferimenti, e i valori restituiti sono condivisi tra tutte le istanze - se aggiorni in un'area, tutti ottengono lo stesso ..
Acecool

-6

Molte delle risposte fornite richiedono così tante righe per proprietà, cioè / e / o - quella che considererei un'implementazione brutta o noiosa a causa della ripetitività richiesta per più proprietà, ecc. Preferisco continuare a far bollire le cose / semplificarle finché non non può più essere semplificato o finché non serve a molto farlo.

In breve: nei lavori completati, se ripeto 2 righe di codice, in genere lo converto in una funzione di aiuto a riga singola, e così via ... Semplifico argomenti matematici o dispari come (start_x, start_y, end_x, end_y) a (x, y, w, h) cioè x, y, x + w, y + h (a volte richiedono min / max o se w / h sono negativi e l'implementazione non piace, sottraggerò da x / ye abs w / h. ecc ..).

Ignorare i getter / setter interni è un buon modo per andare, ma il problema è che devi farlo per ogni classe, o genitore della classe a quella base ... Questo non funziona per me come preferirei essere libero di scegliere i figli / genitori per l'eredità, i nodi figlio, ecc.

Ho creato una soluzione che risponde alla domanda senza utilizzare un tipo di dati Dict per fornire i dati poiché trovo che sia noioso inserire i dati, ecc ...

La mia soluzione richiede di aggiungere 2 righe extra sopra la tua classe per creare una classe base per la classe a cui desideri aggiungere le proprietà, quindi 1 riga per e hai la possibilità di aggiungere callback per controllare i dati, informarti quando i dati cambiano , limitare i dati che possono essere impostati in base al valore e / o al tipo di dati e molto altro ancora.

Hai anche la possibilità di utilizzare _object.x, _object.x = value, _object.GetX (), _object.SetX (value) e vengono gestiti in modo equivalente.

Inoltre, i valori sono gli unici dati non statici assegnati all'istanza della classe, ma la proprietà effettiva è assegnata alla classe, il che significa che le cose che non vuoi ripetere, non devono essere ripetute ... Tu può assegnare un valore predefinito in modo che il getter non ne abbia bisogno ogni volta, sebbene ci sia un'opzione per sovrascrivere il valore predefinito predefinito, e c'è un'altra opzione in modo che il getter restituisca il valore memorizzato non elaborato sovrascrivendo i rendimenti predefiniti (nota: questo metodo significa che il valore grezzo viene assegnato solo quando viene assegnato un valore, altrimenti è None - quando il valore è Reset, allora assegna Nessuno, ecc.)

Ci sono anche molte funzioni di supporto: la prima proprietà che viene aggiunta aggiunge 2 o più helper alla classe per fare riferimento ai valori dell'istanza ... Sono ResetAccessors (_key, ..) vararg ripetuti (tutti possono essere ripetuti usando i primi argomenti denominati ) e SetAccessors (_key, _value) con la possibilità di aggiungerne altri alla classe principale per aiutare nell'efficienza - quelli pianificati sono: un modo per raggruppare le funzioni di accesso insieme, quindi se si tende a reimpostarne alcune alla volta, ogni volta , puoi assegnarli a un gruppo e reimpostarlo invece di ripetere ogni volta i tasti nominati e altro ancora.

L'istanza / valore non elaborato archiviato viene archiviato in classe., la classe. fa riferimento alla classe Accessor che contiene variabili / valori / funzioni statiche per la proprietà. _classe. è la proprietà stessa che viene chiamata quando si accede tramite la classe di istanza durante l'impostazione / acquisizione, ecc.

L'Accessor _class .__ punta alla classe, ma poiché è interna deve essere assegnata nella classe, motivo per cui ho scelto di utilizzare __Name = AccessorFunc (...) per assegnarla, una singola riga per proprietà con molti optional argomenti da usare (usando vararg con chiave perché sono più facili ed efficienti da identificare e mantenere) ...

Creo anche molte funzioni, come accennato, alcune delle quali utilizzano le informazioni sulle funzioni accessorie, quindi non è necessario chiamarle (poiché al momento è un po 'scomodo - in questo momento è necessario utilizzare _class. .FunctionName (_class_instance , args) - Sono andato in giro usando lo stack / trace per afferrare il riferimento all'istanza per acquisire il valore aggiungendo le funzioni che eseguono questa maratona di bit, o aggiungendo le funzioni di accesso all'oggetto e usando self (chiamato questo per indicare che per l'istanza e per mantenere l'accesso a self, al riferimento alla classe AccessorFunc e ad altre informazioni dall'interno delle definizioni di funzione).

Non è ancora finito, ma è un fantastico appoggio. Nota: se non usi __Name = AccessorFunc (...) per creare le proprietà, non avrai accesso al tasto __ anche se lo definisco all'interno della funzione init. Se lo fai, non ci sono problemi.

Inoltre: si noti che il nome e la chiave sono diversi ... Il nome è "formale", utilizzato nella creazione del nome della funzione e la chiave è per l'archiviazione e l'accesso ai dati. cioè _class.x dove x minuscola è la chiave, il nome sarebbe X maiuscolo in modo che GetX () sia la funzione invece di Getx () che sembra un po 'strano. questo consente a self.x di funzionare e apparire appropriato, ma anche di consentire a GetX () di apparire appropriato.

Ho una classe di esempio impostata con chiave / nome identici e diversi da mostrare. molte funzioni di supporto create per produrre i dati (Nota: non tutto questo è completo) in modo da poter vedere cosa sta succedendo.

L'elenco corrente di funzioni utilizzando key: x, name: X restituisce come:

Questo non è affatto un elenco completo - ce ne sono alcuni che non lo hanno fatto al momento della pubblicazione ...

_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

Alcuni dei dati in uscita sono:

Questo è per una nuova classe creata utilizzando la classe Demo senza alcun dato assegnato oltre al nome (quindi può essere emesso) che è _foo, il nome della variabile che ho usato ...

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

E questo è dopo aver assegnato tutte le proprietà _foo (eccetto il nome) i seguenti valori nello stesso ordine: '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)                                                                                                                                                                                                   |

Si noti che a causa di tipi di dati limitati o limitazioni di valore, alcuni dati non sono stati assegnati: questo è di progettazione. Il setter proibisce l'assegnazione di tipi di dati o valori errati, anche di essere assegnati come valore predefinito (a meno che non si ignori il comportamento di protezione del valore predefinito)

Il codice non è stato pubblicato qui perché non avevo spazio dopo gli esempi e le spiegazioni ... Anche perché cambierà.

Nota: al momento di questo post, il file è disordinato - questo cambierà. Ma, se lo esegui in Sublime Text e lo compili, o lo esegui da Python, compilerà e sputerà un sacco di informazioni: la parte AccessorDB non viene eseguita (che verrà utilizzata per aggiornare Print Getters e GetKeyOutput helper funzioni insieme ad essere cambiato in una funzione di istanza, probabilmente messo in una singola funzione e rinominato - cercalo ..)

Avanti: Non tutto è richiesto per l'esecuzione - molte delle cose commentate in fondo servono per ulteriori informazioni usate per il debug - potrebbe non essere lì quando lo scarichi. Se lo è, dovresti essere in grado di decommentare e ricompilare per ottenere maggiori informazioni.

Sto cercando una soluzione per aver bisogno di MyClassBase: pass, MyClass (MyClassBase): ... - se conosci una soluzione - pubblicala.

L'unica cosa necessaria nella classe sono le righe __ - la str è per il debug come l' init - possono essere rimosse dalla classe Demo ma sarà necessario commentare o rimuovere alcune delle righe sottostanti (_foo / 2/3 ) ..

Le classi String, Dict e Util in alto fanno parte della mia libreria Python - non sono complete. Ho copiato alcune cose di cui avevo bisogno dalla libreria e ne ho create alcune nuove. Il codice completo si collegherà alla libreria completa e lo includerà insieme a fornire chiamate aggiornate e rimuovere il codice (in realtà, l'unico codice rimasto sarà la classe Demo e le istruzioni di stampa - il sistema AccessorFunc verrà spostato nella libreria). ..

Parte del file:

##
## 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 = { } )

Questa bellezza rende incredibilmente facile creare nuove classi con proprietà aggiunte dinamicamente con AccessorFuncs / callback / data-type / value enforcement, ecc.

Per ora, il collegamento è a (Questo collegamento dovrebbe riflettere le modifiche al documento.): Https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0

Inoltre: se non usi Sublime Text, lo consiglio su Notepad ++, Atom, Visual Code e altri a causa delle corrette implementazioni di threading che lo rendono molto, molto più veloce da usare ... Sto anche lavorando su un codice simile all'IDE sistema di mappatura per esso - dai un'occhiata a: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (Aggiungi prima Repo in Package Manager, quindi Installa Plugin - quando la versione 1.0.0 è pronta, aggiungerò all'elenco principale dei plugin ...)

Spero che questa soluzione aiuti ... e, come sempre:

Solo perché funziona, non lo fa bene - Josh 'Acecool' Moser


Volevo aggiungere una rapida visualizzazione
dell'aspetto

Apparentemente questo sta ricevendo molto odio, il che è fonte di confusione. Fa esattamente ciò che richiede l'OP: aggiungere dinamicamente proprietà a un oggetto. Aggiunge anche funzioni di supporto, che non devono essere incluse - forse è per questo che sta ricevendo odio - e si assicura anche che lo sviluppatore abbia un modo semplice per accedere alla proprietà (.x) che viene elaborata tramite il getter, il valore grezzo memorizzato (._x) che può essere Nessuno quando .x restituisce il valore predefinito o qualcos'altro, e un modo per accedere all'accessor per usare gli helper, modificare le cose, ecc. (.__ x) ....
Acecool
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.