Comment ajouter dynamiquement une propriété à une classe?

216

Le but est de créer une classe fictive qui se comporte comme un jeu de résultats db.

Ainsi, par exemple, si une requête de base de données revient, en utilisant une expression dict {'ab':100, 'cd':200}, alors je voudrais voir:

>>> dummy.ab
100

Au début, j'ai pensé que je pourrais peut-être le faire de cette façon:

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

mais c.ab renvoie un objet de propriété à la place.

Remplacement de la setattrligne park = property(lambda x: vs[i]) ne sert à rien.

Alors, quelle est la bonne façon de créer une propriété d'instance au moment de l'exécution?

PS Je connais une alternative présentée dans Comment la __getattribute__méthode est-elle utilisée?

Anthony Kong
la source
2
Il y a quelques fautes de frappe dans votre code: la définition de fn_readonly a besoin de a :et de __init__références self.fn_readyonly.
mhawke
Vous avez raison. J'ai ajouté cette fonction setter à la dernière minute afin de souligner la raison de la création d'une propriété en runtime.
Anthony Kong
Le principal problème que j'ai eu avec la création de propriétés à l'initialisation était que, dans certains cas, si j'appelais les assistants rapidement après, ou s'il y avait un problème, j'obtiendrais une erreur selon laquelle ils n'existaient pas malgré le fait. Dans ma solution ci-dessous, je crée 2 classes. L'un en tant que base / parent (que j'essaie de trouver une solution à éviter), et l'objet principal, qui étend la base / parent. Ensuite, dans l'objet principal, sans initialisation, j'appelle mon créateur AccessorFunc qui crée les propriétés, les fonctions d'assistance, etc.
Acecool
ie: classe ExampleBase: pass; Exemple de classe (ExampleBase): __x = Accessor (ExampleBase, 'x', 'X', 123); --- qui créerait une propriété sous x et des fonctions nommées en utilisant X donc GetX, SetX, et plus ... et .x, ._x et .__ x pour la propriété. Donc .x est la propriété elle-même pour les données à traverser (obtenir / paramétrer via self.x = 123; ou self.x pour sortir). J'ai utilisé self._x pour les données RAW stockées afin qu'elles puissent être facilement accessibles car j'ai également permis d'attribuer des valeurs par défaut, sans les définir dans les données stockées. donc _x pourrait être None et .x pourrait retourner 123. et .__ x liés à l'
accesseur
Voici un lien vers la version de base qui crée des propriétés dynamiques et des fonctions dynamiques - le fichier a un tas de liens vers d'autres versions. L'un est le système AccessorFunc utilisant une fonction pour créer des assistants (un pour les fonctions, un pour les propriétés, un pour les deux en tant qu'éléments individuels - il n'utilise donc pas de raccourcissement de code dans quoi que ce soit dans ce fichier). S'il manque quelque chose, l'un des les autres fichiers l'ont: dropbox.com/s/phnnuavssmzeqrr/dynamic_properties_simple.py?dl=0
Acecool

Réponses:

334

Je suppose que je devrais développer cette réponse, maintenant que je suis plus âgé et plus sage et que je sais ce qui se passe. Mieux vaut tard que jamais.

Vous pouvez ajouter une propriété à une classe de manière dynamique. Mais c'est le hic: vous devez l'ajouter à la classe .

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

A propertyest en fait une simple implémentation d'une chose appelée descripteur . C'est un objet qui fournit une gestion personnalisée pour un attribut donné, sur une classe donnée . Un peu comme un moyen d' iféliminer un énorme arbre __getattribute__.

Quand je demande foo.bdans l'exemple ci - dessus, Python voit que la bdéfinition des instruments de classe le protocole descripteur -qui signifie simplement qu'il est un objet avec un __get__, __set__ou __delete__méthode. Le descripteur revendique la responsabilité de gérer cet attribut, donc Python appelle Foo.b.__get__(foo, Foo), et la valeur de retour vous est renvoyée en tant que valeur de l'attribut. Dans le cas de propertychacune de ces méthodes appelle simplement le fget, fsetou fdelvous avez passé au propertyconstructeur.

Les descripteurs sont vraiment la façon dont Python expose la plomberie de toute son implémentation OO. En fait, il existe un autre type de descripteur encore plus courant que 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>

L'humble méthode n'est qu'un autre type de descripteur. Ses __get__pointes sur l'instance appelante comme premier argument; en effet, il fait ceci:

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

Quoi qu'il en soit, je soupçonne que c'est pourquoi les descripteurs ne fonctionnent que sur les classes: ils sont une formalisation des éléments qui alimentent les classes en premier lieu. Ils sont même l'exception à la règle: vous pouvez évidemment attribuer des descripteurs à une classe, et les classes sont elles-mêmes des instances de type! En fait, essayer de lire les Foo.bappels fixesproperty.__get__ ; il est juste idiomatique que les descripteurs se renvoient lorsqu'ils sont accédés en tant qu'attributs de classe.

Je pense que c'est assez cool que pratiquement tout le système OO de Python puisse être exprimé en Python. :)

Oh, et j'ai écrit un article de blog verbeux sur les descripteurs il y a quelque temps si cela vous intéresse.

Évoli
la source
35
Pas besoin d'ajouter la méthode add_property. setattr (Foo, 'name', property (func))
Courtney D
8
Votre "Mais c'est le hic ..." m'a juste sauvé plusieurs heures de travail. Je vous remercie.
Matt Howell du
2
Si vous souhaitez définir une propriété sur une seule instance, vous pouvez créer une classe au moment de l'exécution et modifier __class__ .
Wilfred Hughes
1
qu'en est-il de @ myproperty.setter? Comment l'ajouter dinamicalement?
LRMAAX
Vous n'avez pas besoin d'ajouter une propriété à un objet initialisé. Cela peut signifier que cela ne colle qu'à l'instance, mais je devrais vérifier à nouveau. Je sais que je suis tombé sur un problème similaire où mes propriétés dynamiques n'étaient que des instances, j'ai également fini avec une configuration statique et celle que je voulais qui concerne l'objet afin que les futures initialisations les utilisent. Mon message est ci-dessous et il crée des fonctions d'aide et des moyens faciles d'accéder facilement à tout. .x pour la propriété, ._x pour les données brutes utilisées par le getter / setter (qui peut être None) et .__ x pour l'objet accesseur.
Acecool
57

Le but est de créer une classe fictive qui se comporte comme un jeu de résultats db.

Donc, ce que vous voulez, c'est un dictionnaire où vous pouvez épeler a ['b'] comme ab?

C'est facile:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__
bobince
la source
1
dans une configuration plus générale, cela n'a qu'un but limité. si le dict a une hiérarchie à plusieurs niveaux, comme d = {'a1': {'b': 'c'}, 'a2': ...}, alors pendant que vous pouvez faire d.a1 ou d.a2, vous pouvez ' t do d.a1.b
Shreyas
1
Une chose à garder à l' esprit est que cela permet de définir des valeurs d'attributs pour les attributs avec le même nom que les méthodes de dict ou des attributs, mais ne permet pas de récupérer les valeurs de la même façon: d.items = 1, les d.itemsretours <built-in method items of atdict object at ...>. Vous pouvez toujours faire d["items"]ou utiliser à la __getattribute__place de __getattr__, mais cela empêche d'utiliser la plupart des méthodes du dict.
Marcono1234
Utilisez simplement la bibliothèque munch ! (Fork of bunch)
Brian Peterson
38

Il semble que vous puissiez résoudre ce problème beaucoup plus simplement avec un namedtuple, car vous connaissez à l'avance la liste complète des champs.

from collections import namedtuple

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

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

foo2 = Foo()  # error

Si vous avez absolument besoin d'écrire votre propre setter, vous devrez faire la métaprogrammation au niveau de la classe; property()ne fonctionne pas sur les instances.

Évoli
la source
Bonne idée. Malheureusement, je suis actuellement bloqué avec python 2.4.
Anthony Kong
2
Le gars qui a écrit namedtuplemérite un prix pour le rendre lisse et élégant pour être des principes orientés objet fidèles.
Keith Pinson
4
Désolé, au mieux, cette réponse ne s'applique qu'au cas spécial où une seule classe composée uniquement d'attributs en lecture seule est connue à l'avance. En d'autres termes, je ne pense pas que cela réponde à la question plus large de savoir comment ajouter des propriétés générales - pas seulement en lecture seule - à une classe au moment de l'exécution (pas plus que la version actuelle de l'autre "module complémentaire" ne répond également posté par l'auteur).
martineau
@martineau alors ... passez plus d'arguments à property()? il n'y a rien dans les deux réponses qui soit spécifique aux propriétés en lecture seule.
Évoli
32

Vous n'avez pas besoin d'utiliser une propriété pour cela. Remplacez-les simplement __setattr__pour les rendre en lecture seule.

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
la source
9

Comment ajouter dynamiquement une propriété à une classe python?

Supposons que vous ayez un objet auquel vous souhaitez ajouter une propriété. En règle générale, je souhaite utiliser des propriétés lorsque je dois commencer à gérer l'accès à un attribut dans un code qui a une utilisation en aval, afin de pouvoir conserver une API cohérente. Maintenant, je vais généralement les ajouter au code source où l'objet est défini, mais supposons que vous n'avez pas cet accès, ou que vous devez vraiment choisir dynamiquement vos fonctions par programme.

Créer une classe

En utilisant un exemple basé sur la documentation deproperty , créons une classe d'objet avec un attribut "caché" et créons-en une instance:

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

o = C()

En Python, nous nous attendons à ce qu'il y ait une façon évidente de faire les choses. Cependant, dans ce cas, je vais montrer deux façons: avec la notation décoratrice et sans. Tout d'abord, sans notation décoratrice. Cela peut être plus utile pour l'affectation dynamique des getters, setters ou deleters.

Dynamique (aka Monkey Patching)

Créons-en pour notre classe:

def getx(self):
    return self._x

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

def delx(self):
    del self._x

Et maintenant, nous les attribuons à la propriété. Notez que nous pourrions choisir nos fonctions par programmation ici, répondant à la question dynamique:

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

Et l'utilisation:

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

    I'm the 'x' property.

Décorateurs

Nous pourrions faire la même chose que nous l'avons fait ci-dessus avec la notation décoratrice, mais dans ce cas, nous devons nommer toutes les méthodes du même nom (et je recommanderais de garder la même chose que l'attribut), donc l'affectation programmatique n'est pas aussi triviale que il utilise la méthode ci-dessus:

@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

Et affectez l'objet de propriété avec ses setters et deleters provisionnés à la classe:

C.x = x

Et l'utilisation:

>>> 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
la source
5

J'ai posé une question similaire sur ce post Stack Overflow pour créer une fabrique de classe qui a créé des types simples. Le résultat a été cette réponse qui avait une version fonctionnelle de l'usine de classe. Voici un extrait de la réponse:

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

Vous pouvez utiliser une variante de cela pour créer des valeurs par défaut, ce qui est votre objectif (il y a aussi une réponse à cette question qui traite de cela).

kjfletch
la source
4

Je ne sais pas si je comprends parfaitement la question, mais vous pouvez modifier les propriétés d'instance au moment de l'exécution avec le intégré __dict__de votre 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
Croissant frais
la source
En substance, ma question est de savoir s'il est possible de créer une nouvelle propriété lors de l'exécution. Le consensus semble négatif. Votre suggestion est certainement simple et pratique. (Identique aux autres réponses qui utilisent dict )
Anthony Kong
Une réponse simple serait également:self.__dict__[key] = value
Allan Karlson
4

Pour ceux qui viennent des moteurs de recherche, voici les deux choses que je cherchais en parlant de dynamique propriétés :

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__ est bon si vous voulez mettre des propriétés créées dynamiquement. __getattr__est bon de ne faire quelque chose que lorsque la valeur est nécessaire, comme interroger une base de données. Le combo set / get est bon pour simplifier l'accès aux données stockées dans la classe (comme dans l'exemple ci-dessus).

Si vous ne voulez qu'une seule propriété dynamique, jetez un œil à la fonction intégrée property () .

tleb
la source
4

Vous ne pouvez pas ajouter de nouveau property()à une instance au moment de l'exécution, car les propriétés sont des descripteurs de données. Au lieu de cela, vous devez créer dynamiquement une nouvelle classe ou surcharger __getattribute__afin de traiter les descripteurs de données sur les instances.

Alex Gaynor
la source
C'est faux. Vous pouvez ajouter la propriété à la classe puis y accéder à partir de la méthode.
Ahmed
2

La meilleure façon d'y parvenir est de définir __slots__. De cette façon, vos instances ne peuvent pas avoir de nouveaux attributs.

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

Qui imprime 12

    c.ab = 33

Ça donne: AttributeError: 'C' object has no attribute 'ab'

nosklo
la source
2

Juste un autre exemple pour obtenir l'effet souhaité

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)

Alors maintenant, nous pouvons faire des choses comme:

>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5
lehins
la source
2

Voici une solution qui:

  • Permet de spécifier des noms de propriété sous forme de chaînes , afin qu'ils puissent provenir d'une source de données externe au lieu d'être tous répertoriés dans votre programme.
  • Ajoute les propriétés lorsque la classe est définie , au lieu de chaque fois qu'un objet est créé.

Une fois la classe définie, il vous suffit de faire ceci pour lui ajouter une propriété dynamiquement:

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

Voici un exemple complet, testé en 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)
David Grayson
la source
1

Vous pouvez utiliser le code suivant pour mettre à jour les attributs de classe à l'aide d'un objet dictionnaire:

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
la source
1

C'est un peu différent de ce que voulait OP, mais j'ai secoué mon cerveau jusqu'à ce que j'obtienne une solution de travail, donc je mets ici pour le prochain gars / gal

J'avais besoin d'un moyen de spécifier des setters et des getters dynamiques.

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)

Je connais mes champs à l'avance donc je vais créer mes propriétés. REMARQUE: vous ne pouvez pas faire cette instance PER, ces propriétés existeront sur la classe !!!

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

Essayons tout maintenant ..

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

Est-ce déroutant? Oui, désolé, je n'ai pas pu trouver d'exemples significatifs dans le monde réel. En outre, ce n'est pas pour les cœurs légers.

Javier Buzzi
la source
Si je me souviens bien, j'ai trouvé un moyen pendant tous mes tests pour créer une propriété de type STATIC / g / setter ajouté dynamiquement. Je devrais parcourir toutes mes précédentes - mais être capable d'ajouter quelque chose qui est partagé entre toutes les instances est certainement possible. Quant à la création sur un processus par instance ... Je suis presque sûr que vous pouvez le faire pour qu'une instance ait quelque chose que l'autre n'a pas. Je devrais vérifier, mais je suis tombé sur quelque chose comme ça aussi (dans mes premières tentatives, j'ai fait une erreur qui a créé les fonctions, mais pas toutes les instances en avaient à cause d'un défaut)
Acecool
De plus, toutes les solutions possibles sont les bienvenues car il s'agit d'un dépôt de connaissances. Il est également passionnant de voir les différentes façons dont différentes personnes créent des solutions à un problème. Ma solution fait BEAUCOUP, vous avez réduit cela à quelque chose de plus simple à partager. J'ai aussi fait une petite variante du mien - ça devrait être quelque part dans ce sujet - et je viens de réaliser que ce n'est pas celui que j'ai posté: -) ...
Acecool
0

Cela semble fonctionner (mais voir ci-dessous):

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

Si vous avez besoin d'un comportement plus complexe, n'hésitez pas à modifier votre réponse.

Éditer

Les éléments suivants seraient probablement plus économes en mémoire pour les grands ensembles de données:

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
la source
0

Pour répondre à l'essentiel de votre question, vous souhaitez un attribut en lecture seule à partir d'un dict en tant que source de données immuable:

Le but est de créer une classe fictive qui se comporte comme un jeu de résultats db.

Ainsi, par exemple, si une requête de base de données revient, en utilisant une expression dict {'ab':100, 'cd':200}, alors je verrais

>>> dummy.ab
100

Je vais vous montrer comment utiliser un namedtupledu collectionsmodule pour accomplir cela:

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

Retour 100

Aaron Hall
la source
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)

Et la sortie est:

>> 1
Serhii Khachko
la source
0

Étendre l'idée de 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)

Production:

<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
la source
0

Bien que de nombreuses réponses soient données, je n'ai pu en trouver une qui me satisfasse. J'ai trouvé ma propre solution qui propertyfonctionne pour le cas dynamique. La source pour répondre à la question d'origine:

#!/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
la source
0

Quelque chose qui fonctionne pour moi est le suivant:

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)

Production

a
aa
prosti
la source
-1

J'ai récemment rencontré un problème similaire, la solution que j'ai trouvée utilise __getattr__et __setattr__pour les propriétés que je veux qu'il gère, tout le reste est transmis aux originaux.

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
la source
J'ai examiné cela, mais vous parcourez techniquement une liste dans vos assistants getter et setter. Pour cette raison, chaque appel serait plus lent, car vous le recherchez dans une liste, d'abord, au lieu d'y accéder directement. À moins que Python ne le mappe automatiquement pour vous; c'est possible, mais je ne l'ai pas encore évalué pour être sûr, mais c'est une préoccupation pour moi avant de l'essayer. Deuxièmement, en faisant cela, vous devez définir les assistants d'une autre manière. Vous ne pouvez pas non plus verrouiller les types de données et / ou les valeurs sans vous retrouver avec un grand dictionnaire ou beaucoup de lignes supplémentaires.
Acecool
c'est-à-dire: je devrais soit créer une classe de base à partir de laquelle j'étend tous mes enfants qui utilisent le système, OU je dois ajouter des fonctions magiques s / getattr à tout et dupliquer le système à chaque fois. La déclaration de propriétés signifie également que vous devez les configurer dans un sens et si vous souhaitez bénéficier de la prise en charge supplémentaire, comme je l'ai indiqué, comme le type de données et / ou la protection des valeurs pour autoriser ou empêcher l'attribution de données et d'autres aides , alors vous devez les coder. Certes, vous pouvez rendre le système similaire dans son comportement, mais il se termine là où vous déclarez légèrement différemment et plus volumineux.
Acecool le
-1

Voici l'exemple simple pour créer un objet de propriété par programmation.

#!/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
la source
-2

La seule façon d'attacher dynamiquement une propriété est de créer une nouvelle classe et son instance avec votre nouvelle propriété.

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)
M. Utku ALTINKAYA
la source
1
cela ne semble pas fonctionner. il attribuera le résultat de la propriété, pas la propriété elle-même.
mjallday
Ceci est une erreur. J'attache des propriétés dynamiques à mon système sans jamais avoir à initialiser la classe. Initialiser étant x = Exemple (), puis ajouter la propriété à x.
Acecool
Si vous regardez mon code, vous verrez que j'utilise la classe ExampleBase: pass, puis la classe Example (ExampleBase): ... alors j'attache les propriétés à ExampleBase, car le nom existe alors, et puisque Example s'étend à partir de celui-ci, il a accès à tout. J'utilise le __ var pour que l'accesseur auxiliaire puisse avoir un accès direct aux objets accesseurs, j'utilise _ pour les données stockées (brutes) qui peuvent être Aucune, et pas de soulignement pour la propriété réelle qui passe par le getter. Je peux appeler la fonction getter à l'aide d'une fonction ajoutée dynamiquement ou utiliser la propriété. Tout cela sans avoir d'abord initié.
Acecool
Remarque: je l'ai mentionné - mais ma définition de définition signifie que la référence existe dans l'espace de noms - c'est-à-dire: classe Exemple (objet): pass ... Elle existe, mais n'a pas été initialisée. L'initialisation signifie blah = Exemple (); maintenant l'objet a été «dupliqué» et construit, puis stocké comme référence dans bla. --- Si vous faites cela, alors les fonctions / propriétés ajoutées dynamiquement ne devraient être que dans l'instance - le problème que j'ai eu avec cela était même si les fonctions existaient, il y avait des cas où je recevais une erreur disant qu'ils ne l'étaient pas. Soit une erreur de blocage a arrêté la création, soit une exécution asynchrone.
Acecool
Quelques autres remarques: Il est possible de créer dynamiquement des propriétés et des fonctions d'une manière qui n'existe que par instance. Vous pouvez également les créer pour qu'ils existent pour l'objet (ce que vous souhaitez dans la plupart des cas). Et il y a des cas où les éléments ajoutés sont «statiques», c'est-à-dire les mêmes références, et les valeurs renvoyées sont partagées entre toutes les instances - si vous mettez à jour dans un domaine, tous obtiennent les mêmes ..
Acecool
-6

Un grand nombre des réponses fournies nécessitent autant de lignes par propriété, c'est-à-dire / et / ou - ce que je considérerais comme une implémentation laide ou fastidieuse en raison de la répétitivité requise pour plusieurs propriétés, etc. Je préfère garder les choses en ébullition / les simplifier jusqu'à ce qu'elles ne peut plus être simplifié ou tant que cela ne sert pas à grand chose.

En bref: dans les travaux terminés, si je répète 2 lignes de code, je le convertis généralement en une fonction d'assistance sur une seule ligne, etc. (x, y, w, h) c'est-à-dire x, y, x + w, y + h (nécessitant parfois min / max ou si w / h sont négatifs et que l'implémentation ne l'aime pas, je vais soustraire de x / y et abs w / h, etc.).

Remplacer les getters / setters internes est une bonne façon de procéder, mais le problème est que vous devez le faire pour chaque classe, ou parent de la classe à cette base ... Cela ne fonctionne pas pour moi car je préférerais être libre de choisir les enfants / parents pour l'héritage, les nœuds enfants, etc.

J'ai créé une solution qui répond à la question sans utiliser un type de données Dict pour fournir les données car je trouve cela fastidieux de saisir les données, etc ...

Ma solution vous oblige à ajouter 2 lignes supplémentaires au-dessus de votre classe pour créer une classe de base pour la classe à laquelle vous souhaitez ajouter les propriétés, puis 1 ligne par et vous avez la possibilité d'ajouter des rappels pour contrôler les données, vous informer lorsque les données changent , restreignez les données qui peuvent être définies en fonction de la valeur et / ou du type de données, et bien plus encore.

Vous avez également la possibilité d'utiliser _object.x, _object.x = valeur, _object.GetX (), _object.SetX (valeur) et ils sont gérés de manière équivalente.

De plus, les valeurs sont les seules données non statiques qui sont affectées à l'instance de classe, mais la propriété réelle est affectée à la classe, ce qui signifie que les choses que vous ne voulez pas répéter, n'ont pas besoin d'être répétées ... Vous peut affecter une valeur par défaut afin que le getter n'en ait pas besoin à chaque fois, bien qu'il existe une option pour remplacer la valeur par défaut, et il existe une autre option pour que le getter renvoie la valeur stockée brute en remplaçant les retours par défaut (remarque: cette méthode signifie que la valeur brute n'est affectée que lorsqu'une valeur est affectée, sinon elle est None - lorsque la valeur est Reset, alors elle affecte None, etc.)

Il existe également de nombreuses fonctions d'assistance - la première propriété qui est ajoutée ajoute environ 2 assistants à la classe pour référencer les valeurs d'instance ... Ce sont des varargs ResetAccessors (_key, ..) répétés (tous peuvent être répétés en utilisant les premiers arguments nommés ) et SetAccessors (_key, _value) avec la possibilité d'ajouter plus à la classe principale pour aider à l'efficacité - ceux prévus sont: un moyen de regrouper les accesseurs, donc si vous avez tendance à réinitialiser quelques-uns à la fois, à chaque fois , vous pouvez les affecter à un groupe et réinitialiser le groupe au lieu de répéter les touches nommées à chaque fois, et plus encore.

La valeur stockée instance / raw est stockée dans la classe., la classe. fait référence à la classe d'accesseur qui contient des variables / valeurs / fonctions statiques pour la propriété. _classe. est la propriété elle-même qui est appelée lorsqu'elle est accessible via la classe d'instance lors de la définition / de l'obtention, etc.

L'Accessor _class .__ pointe vers la classe, mais parce qu'il est interne, il doit être affecté dans la classe, c'est pourquoi j'ai choisi d'utiliser __Name = AccessorFunc (...) pour l'attribuer, une seule ligne par propriété avec de nombreux facultatifs arguments à utiliser (en utilisant des varargs à clé car ils sont plus faciles et plus efficaces à identifier et à maintenir) ...

Je crée également de nombreuses fonctions, comme mentionné, dont certaines utilisent les informations de la fonction d'accesseur de sorte qu'elles n'ont pas besoin d'être appelées (car c'est un peu gênant pour le moment - pour le moment, vous devez utiliser _class. .FunctionName (_class_instance , args) - Je me suis servi de la pile / trace pour saisir la référence d'instance pour saisir la valeur en ajoutant les fonctions qui exécutent ce bit marathon, ou en ajoutant les accesseurs à l'objet et en utilisant self (nommé ceci pour indiquer qu'ils 'pour l'instance et pour conserver l'accès à soi, à la référence de classe AccessorFunc et à d'autres informations dans les définitions de fonction).

Ce n'est pas tout à fait fait, mais c'est une prise de pied fantastique. Remarque: Si vous n'utilisez pas __Name = AccessorFunc (...) pour créer les propriétés, vous n'aurez pas accès à la clé __ même si je la définis dans la fonction init. Si vous le faites, il n'y a aucun problème.

Notez également que le nom et la clé sont différents ... Le nom est «formel», utilisé dans la création de nom de fonction, et la clé est pour le stockage et l'accès aux données. c'est-à-dire _class.x où x minuscule est la clé, le nom serait X majuscule pour que GetX () soit la fonction au lieu de Getx () qui semble un peu étrange. cela permet à self.x de fonctionner et d'avoir l'air approprié, mais également d'autoriser GetX () et d'avoir l'air approprié.

J'ai un exemple de classe configuré avec une clé / un nom identique et différent à afficher. un grand nombre de fonctions d'assistance créées afin de sortir les données (Remarque: tout cela n'est pas terminé) afin que vous puissiez voir ce qui se passe.

La liste actuelle des fonctions utilisant la touche: x, nom: X sort comme:

Ce n'est en aucun cas une liste complète - il y en a quelques-uns qui ne l'ont pas encore fait au moment de la publication ...

_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

Certaines des données produites sont:

Il s'agit d'une toute nouvelle classe créée à l'aide de la classe Demo sans aucune donnée autre que le nom (afin qu'elle puisse être sortie) qui est _foo, le nom de variable que j'ai utilisé ...

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

Et ceci après avoir assigné à toutes les propriétés _foo (sauf le nom) les valeurs suivantes dans le même ordre: '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)                                                                                                                                                                                                   |

Notez qu'en raison de types de données restreints ou de restrictions de valeurs, certaines données n'ont pas été affectées - c'est par conception. Le setter interdit les mauvais types de données ou valeurs d'être attribués, même d'être assignés comme valeur par défaut (sauf si vous remplacez le comportement de protection de la valeur par défaut)

Le code n'a pas été posté ici car je n'avais pas de place après les exemples et explications ... Aussi parce qu'il va changer.

Veuillez noter: au moment de cette publication, le fichier est en désordre - cela va changer. Mais, si vous l'exécutez dans Sublime Text et le compilez, ou l'exécutez à partir de Python, il compilera et crachera une tonne d'informations - la partie AccessorDB n'est pas terminée (qui sera utilisée pour mettre à jour les Getters d'impression et l'assistant GetKeyOutput fonctions en plus d'être changées en fonction Instance, probablement mises en une seule fonction et renommées - recherchez-la ..)

Suivant: Tout n'est pas nécessaire pour qu'il fonctionne - une grande partie des éléments commentés en bas sont pour plus d'informations utilisées pour le débogage - il peut ne pas être là lorsque vous le téléchargez. Si c'est le cas, vous devriez pouvoir décommenter et recompiler pour obtenir plus d'informations.

Je cherche un moyen de contourner le besoin de MyClassBase: pass, MyClass (MyClassBase): ... - si vous connaissez une solution - postez-la.

La seule chose nécessaire dans la classe sont les lignes __ - la chaîne est pour le débogage tout comme l' init - elles peuvent être supprimées de la classe de démonstration mais vous devrez commenter ou supprimer certaines des lignes ci-dessous (_foo / 2/3 ) ..

Les classes String, Dict et Util en haut font partie de ma bibliothèque Python - elles ne sont pas complètes. J'ai copié quelques éléments dont j'avais besoin dans la bibliothèque et j'en ai créé quelques nouveaux. Le code complet sera lié à la bibliothèque complète et l'inclura avec la fourniture d'appels mis à jour et la suppression du code (en fait, le seul code restant sera la classe de démonstration et les instructions d'impression - le système AccessorFunc sera déplacé vers la bibliothèque). ..

Partie du dossier:

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

Cette beauté rend incroyablement facile la création de nouvelles classes avec des propriétés ajoutées dynamiquement avec AccessorFuncs / rappels / type de données / application de valeur, etc.

Pour l'instant, le lien est à (Ce lien doit refléter les modifications apportées au document.): Https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0

Aussi: Si vous n'utilisez pas Sublime Text, je le recommande par-dessus Notepad ++, Atom, Visual Code et autres en raison des implémentations de threading appropriées, ce qui le rend beaucoup, beaucoup plus rapide à utiliser ... Je travaille également sur un code de type IDE système de cartographie pour cela - jetez un oeil à: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (Ajoutez d'abord Repo dans le gestionnaire de packages, puis installez le plugin - lorsque la version 1.0.0 est prête, je vais ajouter à la liste principale des plugins ...)

J'espère que cette solution aide ... et, comme toujours:

Tout simplement parce que cela fonctionne, ne fait pas les choses correctement - Josh 'Acecool' Moser

Acecool
la source
Je voulais ajouter un affichage rapide de ce à quoi ressemblait la classe pour que vous n'ayez pas besoin d'ouvrir le fichier de code, mais les commentaires ne semblent pas le prendre en charge.
Acecool
Apparemment, cela suscite beaucoup de haine, ce qui prête à confusion. Il fait exactement ce que l'OP demande - ajouter dynamiquement des propriétés à un objet. Il ajoute également des fonctions d'assistance, qui ne doivent pas être incluses - c'est peut-être pour cela qu'il devient haineux - et il garantit également que le développeur a un moyen facile d'accéder à la propriété (.x) qui est traitée via le getter, le valeur brute stockée (._x) qui peut être None lorsque .x renvoie la valeur par défaut ou autre chose, et un moyen d'accéder à l'accesseur pour utiliser des assistants, changer des choses, etc. (.__ x) ....
Acecool