Uma resposta curta: use proxy_tools
O proxy_tools
pacote tenta fornecer @module_property
funcionalidade.
É instalado com
pip install proxy_tools
Usando uma ligeira modificação do exemplo de @Marin, the_module.py
colocamos
from proxy_tools import module_property
@module_property
def thing():
print(". ", end='') # Prints ". " on each invocation
return 'hello'
Agora a partir de outro script, posso fazer
import the_module
print(the_module.thing)
# . hello
Comportamento inesperado
Esta solução possui algumas ressalvas. Ou seja, nãothe_module.thing
é uma string ! É um proxy_tools.Proxy
objeto cujos métodos especiais foram substituídos para que imite uma string. Aqui estão alguns testes básicos que ilustram o ponto:
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']
Internamente, a função original é armazenada em the_module.thing._Proxy__local
:
print(res._Proxy__local)
# <function thing at 0x7f729c3bf680>
Pensamentos adicionais
Honestamente, estou perplexo sobre por que os módulos não têm essa funcionalidade incorporada. Acho que o ponto crucial da questão é que the_module
é uma instância da types.ModuleType
classe. Definir uma "propriedade de módulo" equivale a definir uma propriedade em uma instância dessa classe, em vez da types.ModuleType
própria classe. Para mais detalhes, veja esta resposta .
Na verdade, podemos implementar propriedades da types.ModuleType
seguinte maneira, embora os resultados não sejam bons. Não podemos modificar diretamente os tipos integrados, mas podemos amaldiçoá- los:
# 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__}'))
Isso nos dá uma propriedade que existe em todos os módulos. É um pouco difícil de manejar, pois quebramos o comportamento da configuração em todos os módulos:
import sys
print(sys.thing2)
# hi from sys
sys.thing2 = 5
# AttributeError: can't set attribute
__getattr__
em um módulo para uma solução mais moderna.