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 setattr
ligne 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?
python
properties
runtime
monkeypatching
Anthony Kong
la source
la source
:
et de__init__
référencesself.fn_readyonly
.Réponses:
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 .
A
property
est 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.b
dans l'exemple ci - dessus, Python voit que lab
dé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 appelleFoo.b.__get__(foo, Foo)
, et la valeur de retour vous est renvoyée en tant que valeur de l'attribut. Dans le cas deproperty
chacune de ces méthodes appelle simplement lefget
,fset
oufdel
vous avez passé auproperty
constructeur.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
.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: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 lesFoo.b
appels 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.
la source
setattr (Foo, 'name', property (func))
Donc, ce que vous voulez, c'est un dictionnaire où vous pouvez épeler a ['b'] comme ab?
C'est facile:
la source
d.items = 1
, lesd.items
retours<built-in method items of atdict object at ...>
. Vous pouvez toujours faired["items"]
ou utiliser à la__getattribute__
place de__getattr__
, mais cela empêche d'utiliser la plupart des méthodes du dict.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.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.la source
namedtuple
mérite un prix pour le rendre lisse et élégant pour être des principes orientés objet fidèles.property()
? il n'y a rien dans les deux réponses qui soit spécifique aux propriétés en lecture seule.Vous n'avez pas besoin d'utiliser une propriété pour cela. Remplacez-les simplement
__setattr__
pour les rendre en lecture seule.Tada.
la source
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 de
property
, créons une classe d'objet avec un attribut "caché" et créons-en une instance: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:
Et maintenant, nous les attribuons à la propriété. Notez que nous pourrions choisir nos fonctions par programmation ici, répondant à la question dynamique:
Et l'utilisation:
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:
Et affectez l'objet de propriété avec ses setters et deleters provisionnés à la classe:
Et l'utilisation:
la source
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:
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).
la source
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:la source
self.__dict__[key] = value
Pour ceux qui viennent des moteurs de recherche, voici les deux choses que je cherchais en parlant de dynamique propriétés :
__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 () .
la source
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.la source
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.Qui imprime
12
Ça donne:
AttributeError: 'C' object has no attribute 'ab'
la source
Juste un autre exemple pour obtenir l'effet souhaité
Alors maintenant, nous pouvons faire des choses comme:
la source
Voici une solution qui:
Une fois la classe définie, il vous suffit de faire ceci pour lui ajouter une propriété dynamiquement:
Voici un exemple complet, testé en Python 3:
la source
Vous pouvez utiliser le code suivant pour mettre à jour les attributs de classe à l'aide d'un objet dictionnaire:
la source
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.
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 !!!
Essayons tout maintenant ..
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.
la source
Cela semble fonctionner (mais voir ci-dessous):
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:
la source
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:
Je vais vous montrer comment utiliser un
namedtuple
ducollections
module pour accomplir cela:Retour
100
la source
Et la sortie est:
la source
Étendre l'idée de kjfletch
Production:
la source
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
property
fonctionne pour le cas dynamique. La source pour répondre à la question d'origine:la source
Quelque chose qui fonctionne pour moi est le suivant:
Production
la source
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.la source
Voici l'exemple simple pour créer un objet de propriété par programmation.
la source
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é.
la source
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 ...
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é ...
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
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:
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
la source