J'ai besoin d'ajouter dynamiquement un attribut (contenant un tuple ou un objet) aux objets Python. Cela fonctionne pour les classes Python écrites par moi, mais pas pour les classes intégrées.
Considérez le programme suivant:
X = np.random.random(size=(2,3)) X.__my_hidden_field = (3,1) setattr(X, '__my_hidden_field', (3,1))
Ceci imprime correctement (1, 1) (2, 1)
. Cependant, le programme suivant ne fonctionne pas.
import numpy as np class My_Class(): pass my_obj = My_Class() my_obj2 = My_Class() my_obj.__my_hidden_field = (1,1) my_obj2.__my_hidden_field = (2,1) print(my_obj.__my_hidden_field, my_obj2.__my_hidden_field)
Les deux lignes ci-dessus génèrent l'erreur suivante # AttributeError: l'objet 'numpy.ndarray' n'a pas d'attribut '__my_hidden_field'
Maintenant, la raison trouvée à partir de ces questions (c'est-à-dire Attribution d'attributs à l'objet intégré , Impossible de définir les attributs de l'objet classe , python: ajouter dynamiquement des attributs à un build -in class ) est que Python ne permet pas d'ajouter dynamiquement des attributs aux objets built_in.
Extrait de la réponse: https://stackoverflow.com/a/22103924/8413477
Ceci est interdit intentionnellement pour éviter les modifications fatales accidentelles des types intégrés (fatales pour des parties du code auxquelles vous n'avez jamais pensé). En outre, cela est fait pour empêcher les modifications d'affecter différents interprètes résidant dans l'espace d'adressage, car les types intégrés (contrairement aux classes définies par l'utilisateur) sont partagés entre tous ces interpréteurs.
Cependant, toutes les réponses sont assez anciennes, et j'ai vraiment besoin de faire cela pour mon projet de recherche.
Il existe un module qui permet d'ajouter des méthodes à la classe intégrée: https://pypi.org/project/forbiddenfruit/
Cependant, il ne permet pas d'ajouter des objets / attributs à chaque objet.
Avez-vous de l'aide?
3 Réponses :
Oui, c'est possible, c'est l'une des choses les plus cool de python, en Python, toutes les classes sont créées par la classe type
Vous pouvez lire en détail ici , mais ce que vous devez faire est ceci
In [58]: My_Class = type("My_Class", (My_Class,), {"__my_hidden_field__": X}) In [59]: My_Class.__my_hidden_field__ Out[59]: array([[0.73998002, 0.68213825, 0.41621582], [0.05936479, 0.14348496, 0.61119082]])
* Modifié car l'héritage était manquant, vous devez passer la classe d'origine comme deuxième argument (dans le tuple) pour qu'elle se mette à jour, sinon elle réécrit simplement la classe)
cela ne fonctionnera pas pour les classes intégrées. aussi j'ai besoin d'ajouter des informations aux objets plutôt qu'aux classes
Vous voulez probablement lowref.WeakKeyDictionary
. À partir du doc,
Cela peut être utilisé pour associer des données supplémentaires à un objet appartenant à d'autres parties d'une application sans ajouter d'attributs à ces objets.
Comme un attribut, et contrairement à un dict simple, cela permet aux objets d'être récupérés lorsqu'il n'y a pas d'autres références à celui-ci.
Vous recherchez le champ avec
X._my_hidden_field
au lieu de
my_hidden_field[X]
Deux mises en garde: Premièrement, comme une clé faible peut être supprimée à tout moment sans avertissement, vous ne devrait pas parcourir un WeakKeyDictionary
. La recherche d'un objet auquel vous avez une référence est cependant très bien. Et deuxièmement, vous ne pouvez pas faire de faible référence à un type d'objet écrit en C qui n'a pas de slot pour lui (vrai pour de nombreux intégrés), ou à un type écrit en Python qui n'autorise pas un __weakref __ (généralement dû à
__slots__
).
Si cela pose un problème, vous pouvez simplement utiliser un dict normal pour ces types, mais vous devrez le nettoyer vous-même.
Ce n'est pas tout à fait __weakref__
qui est nécessaire - les types écrits en C qui prennent en charge les références faibles n'auront généralement pas d'attribut __weakref__
, mais ils auront un espace réservé pour supporter un référencement faible. Vous pouvez vérifier si un objet a un tel espace réservé en vérifiant si son type a un __weakrefoffset__
différent de zéro.
Les fonctions étant un exemple.
Est-il possible d'ajouter des attributs à des objets python intégrés de manière dynamique en Python?
Non, les raisons pour lesquelles vous lisez dans les liens que vous avez publiés, sont les mêmes de nos jours. Mais j'ai sorti une recette qui, je pense, pourrait être le point de départ de votre traceur.
Après avoir beaucoup lu à ce sujet, je suis sorti avec un recette qui n'est peut-être pas la solution complète, mais il semble que vous puissiez commencer à partir d'ici.
La bonne chose à propos de cette recette est qu'elle n'utilise pas de bibliothèques tierces, tout est réalisé avec le bibliothèques standard (Python 3.5, 3.6, 3.7).
Cette recette fera un code comme celui-ci être instrumenté (une simple instrumentation est effectuée ici, c'est juste un pouf du concept) et exécuté.
>>> {1: 2, 3: 4} >>> (0, 0)
Nous devons d'abord ajouter le hidden_field
à tout ce que nous voulons (ceci recette ont été testés uniquement avec des dictionnaires).
Le code suivant reçoit une valeur, trouve son type / classe et le sous-classe afin d'ajouter le hidden_field
mentionné. p >
# target/target.py d = {1: 2} d.update({3: 4}) print(d) # Will print "{1: 2, 3: 4}" print(d.hidden_field) # Will print "(0, 0)"
avec cela en place, vous pouvez:
import ast import os from ast_transformer import AnalyserNodeTransformer # instrument_node need to be in the namespace here. from ast_transformer import instrument_node if __name__ == "__main__": target_path = os.path.join(os.path.dirname(__file__), 'target/target.py') with open(target_path, 'r') as program: # Read and parse the target script. tree = ast.parse(program.read()) # Make transformations. tree = AnalyserNodeTransformer().visit(tree) # Fix locations. ast.fix_missing_locations(tree) # Compile and execute. compiled = compile(tree, filename='target.py', mode='exec') exec(compiled)
À ce stade, nous connaissons déjà un moyen d ' "ajouter une instrumentation à un dictionnaire intégré" mais il n'y a pas de transparence ici .
L'étape suivante consiste à "masquer" l'appel instrument_node
et nous le ferons en utilisant le ast Module Python.
Ce qui suit est un transformateur de nœud AST qui prendra n'importe quel dictionnaire qu'il trouve et l'enveloppera dans un instrument_node
call:
class AnalyserNodeTransformer(ast.NodeTransformer): """Wraps all dicts in a call to instrument_node()""" def visit_Dict(self, node): return ast.Call(func=ast.Name(id='instrument_node', ctx=ast.Load()), args=[node], keywords=[]) return node
Avec ces outils, vous pouvez écrire un script qui:
- Lisez le code cible.
- Analyser le programme.
- Appliquer les modifications AST.
- Compilez-le.
- Et exécutez-le.
d = {1: 2} d = instrument_node(d) d.update({3: 4}) print(d) # Do print "{1: 2, 3: 4}" print(d.hidden_field) # Do print "(0, 0)"
Cela prendra notre code cible, et encapsulera chaque dictionnaire avec un instrument_node ()
et exécutera le résultat d'un tel changement.
Le résultat de l'exécution de ceci sur notre code cible,
def instrument_node(value): VarType = type(value) class AnalyserHelper(VarType): def __init__(self, *args, **kwargs): self.hidden_field = (0, 0) super(AnalyserHelper, self).__init__(*args, **kwargs) return AnalyserHelper(value)
est:
# target/target.py d = {1: 2} d.update({3: 4}) print(d) # Should print "{1: 2, 3: 4}" print(d.hidden_field) # Should print "(0, 0)"
Vous pouvez cloner un exemple fonctionnel ici . p>
Je ne pouvais pas faire fonctionner la classe wrapper AnalyzeHelper pour les classes numpy.ndarray. Comment faire fonctionner cette technique pour les classes intégrées / tierces?
Eh bien, je suppose que pour déclarer un numpy.ndarray
, vous devez passer un appel comme numpy.ndarray (
, vous devez lire dans ast et voyez comment vous pouvez modifier les nœuds qui représentent cet appel. Le fait est que AST est un bon outil pour atteindre le niveau de transparence souhaité.
Pourquoi ne sous-classez-vous pas?
Il est possible d'ajouter des attributs à certains objets, mais pas possible de les ajouter à d'autres objets, qu'ils soient intégrés ou non.
J'essaye d'écrire un traceur pour instrumenter le code python pour l'analyse. Je veux que mon instrumentation soit aussi transparente que possible. et le sous-classement peut / peut ne pas résoudre le problème auquel je n'ai pas encore réfléchi. Même si cela fonctionne, ce sera moins prioritaire! @ Tomothy32
Pour contourner ce problème, que diriez-vous d'utiliser un dictionnaire avec
id (X)
comme clé et la valeur du champ masqué comme valeur de dictionnaire?Interditfruit est un segfault qui attend de se produire, soit dit en passant. Il force brutalement son chemin jusqu'au dict d'un type sans aucune compréhension réelle des conséquences de le faire, et il ne gère aucun des pièges, comme le cache d'attributs de type.
Voici un exemple simple qui sépare actuellement les défauts sur repl.it. (Notez également le résultat silencieusement faux sur le second imprimer, avant que la troisième impression ne détruit complètement tout.)
@gilch: Non, car c'est une autre chose qu'interditfruit ne gère pas.