Há dois problemas básicos que você está enfrentando aqui:
__xxx__
métodos são pesquisados apenas na classe
TypeError: can't set attributes of built-in/extension type 'module'
(1) significa que qualquer solução também precisaria acompanhar qual módulo estava sendo examinado; caso contrário, cada módulo teria o comportamento de substituição de instância; e (2) significa que (1) nem sequer é possível ... pelo menos não diretamente.
Felizmente, sys.modules não é exigente quanto ao que existe lá, então um wrapper funcionará, mas apenas para acesso ao módulo (ou seja import somemodule; somemodule.salutation('world')
, para acesso ao mesmo módulo, você praticamente precisará retirar os métodos da classe de substituição e adicioná-los a globals()
cada método personalizado na classe (eu gosto de usar .export()
) ou com uma função genérica (como as que já estão listadas como respostas). Um aspecto a ser lembrado: se o wrapper estiver criando uma nova instância a cada vez e a solução global não estiver, você acaba com um comportamento sutilmente diferente, e não consegue usar os dois ao mesmo tempo - é um ou outro.
Atualizar
Partida Guido van Rossum :
Na verdade, existe um hack ocasionalmente usado e recomendado: um módulo pode definir uma classe com a funcionalidade desejada e, no final, substituir-se em sys.modules por uma instância dessa classe (ou pela classe, se você insistir , mas isso geralmente é menos útil). Por exemplo:
# module foo.py
import sys
class Foo:
def funct1(self, <args>): <code>
def funct2(self, <args>): <code>
sys.modules[__name__] = Foo()
Isso funciona porque o mecanismo de importação está ativando ativamente esse hack e, como sua etapa final, retira o módulo real dos sys.modules, após carregá-lo. (Não é por acaso. O hack foi proposto há muito tempo e decidimos que gostávamos o suficiente para apoiá-lo nas máquinas de importação.)
Portanto, a maneira estabelecida de realizar o que você deseja é criar uma única classe no seu módulo e, como o último ato do módulo, substituir sys.modules[__name__]
por uma instância da sua classe - e agora você pode jogar com __getattr__
/ __setattr__
/ __getattribute__
conforme necessário.
Nota 1 : Se você usar essa funcionalidade, qualquer outra coisa no módulo, como globais, outras funções etc., será perdida quando a sys.modules
atribuição for feita - portanto, verifique se tudo o que é necessário está dentro da classe de substituição.
Nota 2 : Para dar suporte, from module import *
você deve ter __all__
definido na classe; por exemplo:
class Foo:
def funct1(self, <args>): <code>
def funct2(self, <args>): <code>
__all__ = list(set(vars().keys()) - {'__module__', '__qualname__'})
Dependendo da sua versão do Python, pode haver outros nomes para omitir __all__
. O set()
pode ser omitido se a compatibilidade Python 2 não é necessário.