Como chamo setattr () no módulo atual?

140

O que passo como o primeiro parâmetro " object" para a função setattr(object, name, value), para definir variáveis ​​no módulo atual?

Por exemplo:

setattr(object, "SOME_CONSTANT", 42);

dando o mesmo efeito que:

SOME_CONSTANT = 42

dentro do módulo que contém essas linhas (com o correto object).

Estou gerando vários valores no nível do módulo dinamicamente e, como não posso definir __getattr__no nível do módulo, esse é o meu fallback.

Matt Joiner
fonte

Respostas:

220
import sys

thismodule = sys.modules[__name__]

setattr(thismodule, name, value)

ou, sem usar setattr(que quebra a letra da pergunta, mas satisfaz os mesmos objetivos práticos ;-):

globals()[name] = value

Nota : no escopo do módulo, este último é equivalente a:

vars()[name] = value

que é um pouco mais conciso, mas não funciona de dentro de uma função ( vars()fornece as variáveis ​​do escopo em que é chamado: as variáveis ​​do módulo quando chamadas no escopo global e, em seguida, não há problema em usá-lo R / W, mas a função é variáveis ​​quando chamadas em uma função e, em seguida, devem ser tratadas como R / O - os documentos on-line do Python podem ser um pouco confusos sobre essa distinção específica).

Alex Martelli
fonte
9
Os documentos dão um aviso sobre a modificação de vars (). docs.python.org/library/functions.html#vars . Quando é bom fazer isso?
Unutbu 29/05
2
@ ~ unutbu, eu realmente não diria que está "ok", mas funcionará quando você chamar vars()no escopo no nível do módulo, e não dentro de uma função.
Mike Graham
4
vars()é equivalente a globals()no escopo do módulo (e, portanto, retorna um ditado verdadeiro e modificável), mas ao locals()escopo da função (e, portanto, retorna um pseudodict de nunca ser modificado). Eu uso vars()no escopo do módulo, pois economiza 3 caracteres, uma sílaba, vs seu sinônimo-em-que-scope globals();-)
Alex Martelli
14
Sim, isso teria destruído a otimização mais importante que o compilador Python faz: as variáveis ​​locais de uma função não são mantidas em um ditado, elas estão em um vetor restrito de valores e cada acesso a variável local usa o índice nesse vetor, não uma pesquisa de nome. Para derrotar a otimização, forçando o ditado que você deseja existir, inicie a função com exec '': time uma função com alguns loops substanciais em cada sentido, e você verá a importância dessa otimização central para o desempenho do Python.
Alex Martelli 29/05
3
@ msw, acho que você esqueceu de "praticidade supera pureza" ;-).
Alex Martelli
6

Se você deve definir variáveis ​​de escopo do módulo de dentro do módulo, o que há de errado global?

# my_module.py

def define_module_scoped_variables():
    global a, b, c
    a, b, c = 'a', ['b'], 3

portanto:

>>> import my_module
>>> my_module.define_module_scoped_variables()
>>> a
NameError: name 'a' is not defined
>>> my_module.a
'a'
>>> my_module.b
['b']
msw
fonte
1
Sim, eu sempre (onde "sempre" é definido como os "últimos meses que aprendi Python") achou essa global but not reallydeclaração intrigante. Suponho que possa ser uma relíquia histórica que antecede os espaços de nomes dos módulos.
Msw
1
A pergunta original é perguntar como definir um atributo cujo nome é dado por uma string (a mesma coisa que eu estava procurando atualmente), portanto isso não ajudaria.
Curt
6

No Python 3.7, você poderá usar __getattr__no nível do módulo ( resposta relacionada ).

Por PEP 562 :

def __getattr__(name):
    if name == "SOME_CONSTANT":
        return 42
    raise AttributeError(f"module {__name__} has no attribute {name}")
Trey Hunner
fonte
-1
  1. Você não faria. Você fariaglobals()["SOME_CONSTANT"] = 42
  2. Você não faria. Você armazenaria conteúdo gerado dinamicamente em outro lugar que não um módulo.
Mike Graham
fonte
Sim, SOME_CONSTANTcalculado em tempo de execução não é exatamente constante. E se globals()não estiver disponível para você, você deverá acessar outro módulo para modificar seus atributos; isso deve fazer as pessoas se perguntarem.
Msw
3
Constante e mutável são mutuamente exclusivos. Constante e gerado dinamicamente não são. Os valores que estou gerando são sempre os mesmos e determinados com base em outras "constantes", para economizar em aritmética e digitação da minha parte.
Matt Joiner