Une réponse courte: utiliser proxy_tools
Le proxy_toolspackage tente de fournir@module_property fonctionnalités.
Il s'installe avec
pip install proxy_tools
En utilisant une légère modification de l'exemple de @ Marein, the_module.pynous mettons
from proxy_tools import module_property
@module_property
def thing():
print(". ", end='') # Prints ". " on each invocation
return 'hello'
Maintenant à partir d'un autre script, je peux faire
import the_module
print(the_module.thing)
# . hello
Comportement inattendu
Cette solution n'est pas sans réserves. A savoir, the_module.thingn'est pas une chaîne ! C'est un proxy_tools.Proxyobjet dont les méthodes spéciales ont été surchargées pour imiter une chaîne. Voici quelques tests de base qui illustrent ce point:
res = the_module.thing
# [No output!!! Evaluation doesn't occur yet.]
print(type(res))
# <class 'proxy_tools.Proxy'>
print(isinstance(res, str))
# False
print(res)
# . hello
print(res + " there")
# . hello there
print(isinstance(res + "", str))
# . True
print(res.split('e'))
# . ['h', 'llo']
En interne, la fonction d'origine est stockée dans the_module.thing._Proxy__local:
print(res._Proxy__local)
# <function thing at 0x7f729c3bf680>
Réflexions supplémentaires
Honnêtement, je ne sais pas pourquoi les modules n'ont pas cette fonctionnalité intégrée. Je pense que le nœud du problème est qu'il the_modules'agit d'une instance de la types.ModuleTypeclasse. Définir une "propriété de module" revient à définir une propriété sur une instance de cette classe, plutôt que sur la types.ModuleTypeclasse elle-même. Pour plus de détails, consultez cette réponse .
Nous pouvons réellement implémenter des propriétés types.ModuleTypecomme suit, bien que les résultats ne soient pas excellents. Nous ne pouvons pas modifier directement les types intégrés, mais nous pouvons les maudire :
# python -m pip install forbiddenfruit
from forbiddenfruit import curse
from types import ModuleType
# curse has the same signature as setattr.
curse(ModuleType, "thing2", property(lambda module: f'hi from {module.__name__}'))
Cela nous donne une propriété qui existe sur tous les modules. C'est un peu difficile à manier, car nous cassons le comportement des paramètres dans tous les modules:
import sys
print(sys.thing2)
# hi from sys
sys.thing2 = 5
# AttributeError: can't set attribute
__getattr__sur un module pour une solution plus moderne.