Eh bien, regardons ce qu'est la fonction :
>>> def foo():
... return x
...
>>> foo.x = 777
>>> foo.x
777
>>> foo()
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "<interactive input>", line 2, in foo
NameError: global name 'x' is not defined
>>> dir(foo)
['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__',
'__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__',
'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc',
'func_globals', 'func_name', 'x']
>>> getattr(foo, 'x')
777
Aha ! Donc, l'attribut a été ajouté à l'objet fonction mais il ne le verra pas parce qu'il recherche l'attribut global. x
à la place.
Nous pouvons essayer de saisir le cadre de l'exécution de la fonction et essayer de voir ce qu'il y a là (essentiellement ce qu'Anthony Kong a suggéré mais sans inspect
) :
>>> def foo():
... import sys
... return sys._getframe()
...
>>> fr = foo()
>>> dir(fr)
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'f_back', 'f_builtins', 'f_code', 'f_exc_traceback', 'f_exc_type', 'f_exc_value', 'f_globals', 'f_lasti', 'f_lineno', 'f_locals', 'f_restricted', 'f_trace']
>>> fr.f_locals
{'sys': <module 'sys' (built-in)>}
>>> fr.f_code
<code object foo at 01753020, file "<interactive input>", line 1>
>>> fr.f_code.co_code
'd\x01\x00d\x00\x00k\x00\x00}\x00\x00|\x00\x00i\x01\x00\x83\x00\x00S'
>>> fr.f_code.co_name
'foo'
Aha ! Alors peut-être pouvons-nous obtenir le nom de la fonction à partir du nom du bloc de code et ensuite chercher l'attribut de manière détournée ? Tout à fait :
>>> getattr(fr.f_globals[fr.f_code.co_name], 'x')
777
>>> fr.f_globals[fr.f_code.co_name].x
777
>>> def foo():
... import sys
... frm = sys._getframe()
... return frm.f_globals[frm.f_code.co_name].x
...
>>> foo.x=777
>>> foo()
777
C'est génial ! Mais supporterait-il le renommage et la suppression de la fonction originale ?
>>> g = foo
>>> g.func_name
'foo'
>>> g.func_code.co_name
'foo'
Ah, très douteux. L'objet fonction et son objet code insistent toujours sur le fait qu'ils sont appelés foo
. Bien sûr, c'est là que ça casse :
>>> g.x
777
>>> g.x=888
>>> foo.x
888
>>> g()
888
>>> del foo
>>> g()
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "<interactive input>", line 4, in foo
KeyError: 'foo'
Dang ! Donc, en général, cela ne peut pas être fait par introspection via les cadres d'exécution. Le problème semble être qu'il y a une différence entre objet de la fonction y objet de code - les objets de code sont ce qui est exécuté et c'est juste un attribut func_code
de l'objet-fonction et, en tant que tel, n'a pas accès à l'élément func_dict
où notre attribut x
est :
>>> g
<function foo at 0x0173AE30>
>>> type(g)
<type 'function'>
>>> g.func_code
<code object foo at 017532F0, file "<interactive input>", line 1>
>>> type(g.func_code)
<type 'code'>
>>> g.func_dict
{'x': 888}
Il y a bien sûr d'autres astuces que vous pouvez faire pour que cela semble être une fonction - en particulier l'astuce avec la définition des classes... mais ce n'est pas une fonction en soi. Tout dépend de ce que vous avez vraiment besoin de faire avec ça.
3 votes
Qu'est-ce qui vous empêche d'avoir simplement une fonction avec un seul paramètre ?
3 votes
Space_C0wb0y : les paramètres des fonctions sont hors sujet, c'est une question sur la théorie, pas sur les solutions pragmatiques de la vie réelle.
2 votes
+1 pour m'avoir fait explorer (et apprendre) ce coin des internes de python ;-)
0 votes
Voir aussi : stackoverflow.com/questions/852401/