Comment une fonction d'accès à ses propres attributs?
est-il possible d'accéder à la fonction python les attributs de l'objet à partir de l'intérieur de la portée de la fonction?
par exemple, nous allons avoir
def f():
return SOMETHING
f._x = "foo"
f() # -> "foo"
maintenant, ce quelque CHOSE doit être, si nous voulons avoir la _x contenu d'un attribut "foo" de retour? si c'est encore possible (simplement)
grâce
Mise à JOUR:
j'aimerais les travaux suivants aussi:
g = f
del f
g() # -> "foo"
Mise à JOUR 2:
Déclaration qu'il n'est pas possible (si c'est le cas), et pourquoi, est plus satisfaisant que de fournir un moyen pour de faux, par exemple avec un autre objet que d'une fonction
- Ce qui vous empêche de simplement avoir une fonction avec un paramètre?
- Space_C0wb0y: paramètres de la fonction sont hors sujet, c'est une question de théorie, pas de la vie réelle des solutions pragmatiques
- +1 pour me faire explorer (et d'apprendre) que de coin de python internes 😉
Vous devez vous connecter pour publier un commentaire.
Solution
Faire une de la de la fonction, les arguments par défaut être une référence à la fonction elle-même.
Exemple d'utilisation:
Explication
L'affiche originale voulions une solution qui ne nécessite pas un nom global de recherche. La solution la plus simple
effectue une recherche de la variable globale
f
sur chaque appel, qui ne répond pas aux exigences. Sif
est supprimé, alors la fonction échoue. Le plus compliquéinspect
proposition échoue de la même manière.Ce que nous voulons, c'est d'effectuer la liaison anticipée et de stocker la référence liée à l'intérieur de l'objet lui-même. La suite est conceptuellement ce que nous faisons:
Ci-dessus,
self
est une variable locale, donc pas de mondiale recherche est effectuée. Cependant, nous ne pouvons pas écrire le code comme-est, parce quef
n'est pas encore défini lorsque nous essayons de lier la valeur par défaut deself
à elle. Au lieu de cela, nous avons défini la valeur par défaut aprèsf
est défini.Décorateur
Ici est un simple décorateur de le faire pour vous. Notez que le
self
argument doit être le dernier, à la différence des méthodes, oùself
vient en premier. Cela signifie également que vous devez donner une valeur par défaut si l'un de vos arguments prendre une valeur par défaut.Exemple:
f.func_defaults
fut renomméf.__defaults__
en Python 3 pour des raisons de cohérence? Aussi le des doutes sont partagés par d'autresself
en dernier argument. Suppose que je vais devoir faire l'habitude d'habillage pour obtenir une autre fermetureVous pouvez simplement utiliser une classe pour ce faire
Bien, nous allons regarder ce que la fonction est:
Aha! Si l'attribut a été ajouté à la fonction d'objet, mais il ne sera pas le voir parce qu'il est à la recherche pour le mondial
x
à la place.Nous pouvons essayer d'attraper le cadre de l'exécution de la fonction et de l'essayer pour voir ce qu'il y a (en gros ce que Anthony Kong l'a suggéré, mais w/o
inspect
module):Aha! Alors peut-être que nous pouvons obtenir le nom de la fonction à partir du nom du bloc de code et puis regardez dans le rond-point moyen de l'attribut? Bien sûr:
C'est génial! Mais ne serait-il supporter le changement de nom et la suppression de la fonction d'origine?
Ah, très douteux. La fonction de l'objet et de son code objet insistent toujours sur le fait qu'ils sont appelés
foo
. Bien sûr, ici est l'endroit où il se casse:Dang! Donc, en général, il ne peut pas être fait par le biais de l'introspection par l'exécution des cadres. Les problèmes semble être qu'il y a une différence entre fonction de l'objet et code objet - code les objets sont ce qui est exécuté et c'est seulement un attribut
func_code
de la fonction-objet et en tant que tel n'a pas accès à lafunc_dict
d'un attribut, d'où notre attributx
est:Il y a bien sûr d'autres chicane que vous pouvez faire de sorte qu'il semble que la fonction, en particulier, le truc avec la définition de la classe... mais ce n'est pas une fonction à proprement parler. Tout dépend de ce que vous avez vraiment besoin de faire avec cela.
Comme une solution de contournement, vous pouvez utiliser une fonction de fabrication de réparer votre portée:
Je doute que cela soit le meilleur moyen d'accomplir cela, mais vous pouvez accéder aux attributs à l'aide de la méthode nom de la méthode:
g = f; del f; print(g())
? 🙂x
valeur, bien sûr, puisqueg
est juste une autre référence à quelque chose d'abord référencés seulement parf
Voici un décorateur qui injecte de l'current_fun dans les fonctions globales avant l'exécution de la fonction. C'est tout le hack, mais aussi très efficace.
Voici un exemple d'utilisation
func_globals
doit être remplacé par__globals__
. J'ai étendu le décorateur d'accepter le nom de l'attribut:def introspective(f, attribute_name='self'):
et effectivement renommé@add_self
. Tous les'current_fun'
les occurrences doivent être remplacées par desattribute_name
. Par défaut, le décorateur encapsule l'attributself
dans la fonction. Maintenant, je peux faireself.x
à l'intérieur de fonctions et de classes. Je ne l'utilise jamais mais pourself.logger.info()
qui peut maintenant être bien ajouté à toutes les fonctions, les méthodes et les classes avec des décorateurs.La réponse est assez simple. Utilisez simplement le fait que le nom est recherché au moment de l'exécution, pas le temps de compilation:
Si vous voulez qu'il soit totalement indépendant du nom de la fonction, vous avez besoin d'un cadre magique. Par exemple:
f3=f2; del f2; f3()
inspect
exige que le fichier source soit disponible?return f2._x
. Le code duco_name
est le nom de la fonction où elle a été définie, de sorte que si la fonction est renommé, la recherche dans le dictionnaire mondial sera un échec.Il utilise un peu de hackish approche, mais c'est peut-être le plus correct jusqu'à présent, étant donné qu'il fonctionne avec le
g()
appeler ainsi. Cela fonctionne parce que c'est en s'appuyant sur ce que le bytecode de l'inspection est effectuée par le dis module, comme un raccourci.Il ressemble plus hackish qu'elle ne l'est en partie parce que le
dis.disassemble()
appel imprime sur la sortie standard, donc je redirige le tout dans un StringIO. J'utilisedisassemble()
pour sa fonction de mise en évidence de la dernière instruction (ajouter unprint text
ligne là-bas pour voir à quoi il ressemble) et qui le rend plus facile à saisir de la précédenteLOAD_NAME
et la variable utilisée.Il serait possible d'utiliser un nettoyeur de bytecode d'inspection de la bibliothèque de le faire sans l'aide de la
dis
module, mais cela prouve que c'est possible. Cela pourrait ne pas être l'approche la plus efficace, mais encore une fois peut-être cela fonctionnera dans la plupart des cas. Je n'ai pas passé assez de temps à fureter dans Python internes ou de pseudo-code binaire à savoir si la plupart desCALL_FUNCTION
bytecode sont précédés immédiatement par des instructions pour que la regex astuce choisir.Cela génère la sortie suivante:
Comment sur l'utilisation de la classe au lieu d'une fonction et d'abuser de la
__new__
méthode pour faire de la classe remboursable à partir d'une fonction? Depuis le__new__
méthode est le nom de la classe comme premier paramètre, il peut accéder à tous les attributs de classecomme dans
cela fonctionne comme dans
ensuite, vous pouvez faire
Le problème est que, même si l'objet se comporte comme une fonction, il n'est pas. Donc IDEs ne parviennent pas à fournir vous avec la signature.
Une autre façon d'accomplir ceci est de définir la fonction à l'intérieur d'une autre fonction, et ont l'extérieur de la fonction de retour à l'intérieur. Ensuite, la fonction interne peut accéder par le biais d'une fermeture. Voici un exemple simple:
Alors:
Si il y a une seule méthode nécessaire, mais vous voulez un peu de poids à la classe, avec partage de l'état de classe, plus individuel de l'état de l'instance, vous pouvez essayer de la fermeture de la structure comme ceci:
Ici est une stratégie qui est probablement pire que la
func_defaults
idée, mais il est intéressant néanmoins. C'est hacky mais je ne peux pas penser à quelque chose de pratiquement de mal avec elle.Nous pouvons mettre en œuvre une fonction qui peut se référer à lui-même comme une classe avec un seul
__new__
méthode (la méthode qui, normalement, crée un nouvel objet de cette classe).Peut-être que ce modèle pourrait être utile pour une fonction d'enregistrement...
Il suffit de définir votre fonction à l'intérieur d'une fermeture:
Je l'aime beaucoup.