Com propriedades python, posso torná-lo tal que
obj.y
chama uma função em vez de apenas retornar um valor.
Existe uma maneira de fazer isso com módulos? Eu tenho um caso onde eu quero
module.y
para chamar uma função, em vez de apenas retornar o valor armazenado lá.
python
properties
python-module
Josh Gibson
fonte
fonte
__getattr__
em um módulo para uma solução mais moderna.Respostas:
Apenas instâncias de classes de novo estilo podem ter propriedades. Você pode fazer o Python acreditar que tal instância é um módulo armazenando-o em
sys.modules[thename] = theinstance
. Portanto, por exemplo, o arquivo do módulo m.py pode ser:fonte
types.ModuleType
conforme mostrado na resposta muito semelhante de @Inconhecido?builtins.module
, que por si só é uma instância detype
(que é a definição de classe de novo estilo). O problema é que as propriedades devem estar na classe, não a instância: se você fizer issof = Foo()
,f.some_property = property(...)
, vai falhar da mesma maneira como se você colocá-lo ingenuamente em um módulo. A solução é colocá-lo na classe, mas como você não quer que todos os módulos tenham a propriedade, você cria uma subclasse (veja a resposta de Desconhecido).globals()
(mantendo as chaves intactas, mas redefinindo os valores paraNone
) após a religação do nomesys.modules
é um problema do Python 2 - o Python 3.4 funciona conforme o planejado. Se você precisar acessar o objeto de classe em Py2, adicione, por exemplo,_M._cls = _M
logo após aclass
instrução (ou armazene-o de forma equivalente em algum outro namespace) e acesse-o comoself._cls
nos métodos que o exigem (type(self)
pode ser OK, mas não se você também fizer qualquer subclasse de_M
) .Eu faria isso para herdar adequadamente todos os atributos de um módulo e ser identificado corretamente por isinstance ()
E então você pode inserir isso em sys.modules:
fonte
__file__
que devem ser definidos manualmente, (2) as importações feitas no módulo que contém a classe não serão "visíveis" durante o tempo de execução, etc ...types.ModuleType
, qualquer (estilo novo) classe vai fazer. Exatamente quais atributos de módulo especial você espera herdar?__init__
uma instância e você obterá o comportamento correto ao usarisinstance
.Como o PEP 562 foi implementado em Python> = 3.7, agora podemos fazer isso
arquivo: module.py
uso:
Observe que se você omitir o
raise AttributeError
na__getattr__
função, isso significa que a função termina comreturn None
, então omodule.nosuch
obterá um valor deNone
.fonte
Com base na resposta de John Lin :
Uso (observe o sublinhado inicial), em
the_module.py
:Então:
O sublinhado inicial é necessário para diferenciar a função de propriedade da função original. Não consegui pensar em uma maneira de reatribuir o identificador, pois durante o tempo de execução do decorador, ele ainda não foi atribuído.
Observe que os IDEs não saberão que a propriedade existe e exibirão ondulações vermelhas.
fonte
@property def x(self): return self._x
, acho quedef thing()
sem sublinhado é mais convencional. E você também pode criar um decorador "configurador de propriedades do módulo" em sua resposta?def thing()
sugestão. O problema é que__getattr__
só é chamado para atributos ausentes . Mas depois de@module_property def thing(): …
executado,the_module.thing
é definido, portanto getattr nunca será chamado. Precisamos registrar de alguma formathing
no decorador e, em seguida, excluí-lo do namespace do módulo. Tentei retornarNone
do decorador, masthing
é definido comoNone
. Pode-se fazer,@module_property def thing(): … del thing
mas acho pior do que usarthing()
como uma função__getattribute__
". Obrigado.Um caso de uso típico é: enriquecer um (enorme) módulo existente com alguns (poucos) atributos dinâmicos - sem transformar todo o material do módulo em um layout de classe. Infelizmente, um patch de classe de módulo mais simples como
sys.modules[__name__].__class__ = MyPropertyModule
falha comTypeError: __class__ assignment: only for heap types
. Portanto, a criação do módulo precisa ser reconectada.Essa abordagem faz isso sem ganchos de importação Python, apenas por ter algum prólogo no topo do código do módulo:
Uso:
Nota: algo como
from propertymodule import dynval
irá produzir uma cópia congelada, é claro - correspondendo adynval = someobject.dynval
fonte
Uma resposta curta: use
proxy_tools
O
proxy_tools
pacote tenta fornecer@module_property
funcionalidade.É instalado com
Usando uma ligeira modificação do exemplo de @Marin,
the_module.py
colocamosAgora a partir de outro script, posso fazer
Comportamento inesperado
Esta solução possui algumas ressalvas. Ou seja, não
the_module.thing
é uma string ! É umproxy_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:Internamente, a função original é armazenada em
the_module.thing._Proxy__local
: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 datypes.ModuleType
classe. Definir uma "propriedade de módulo" equivale a definir uma propriedade em uma instância dessa classe, em vez datypes.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: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:
fonte
@module_property
decorador. De modo geral, o@property
decorador embutido é usado quando uma classe é definida, não depois que uma instância dela foi criada, então eu presumiria que o mesmo seria verdadeiro para uma propriedade de módulo e é com a resposta de Alex - lembre-se que esta pergunta pergunta “Os módulos podem ter propriedades da mesma forma que os objetos podem?”. No entanto, é possível adicioná-los depois e modifiquei meu snippet anterior para ilustrar uma maneira que pode ser feita.cached_module_property
, o fato de que__getattr__()
não será mais chamado se o atributo for definido é útil. (semelhante ao quefunctools.cached_property
realiza).