Como criar uma variável entre módulos?

122

A __debug__variável é útil em parte porque afeta todos os módulos. Se eu quiser criar outra variável que funcione da mesma maneira, como eu faria isso?

A variável (sejamos originais e chamemos de 'foo') não precisa ser verdadeiramente global, no sentido de que se eu mudar de foo em um módulo, ela será atualizada em outros. Eu ficaria bem se pudesse definir foo antes de importar outros módulos e eles veriam o mesmo valor para ele.

Dan Homerick
fonte

Respostas:

114

Não subscrevo esta solução de forma alguma. Mas se você adicionar uma variável ao __builtin__módulo, ela estará acessível como se fosse global a partir de qualquer outro módulo que inclua __builtin__- que são todos eles, por padrão.

a.py contém

print foo

b.py contém

import __builtin__
__builtin__.foo = 1
import a

O resultado é que "1" é impresso.

Edit: O __builtin__módulo está disponível como o símbolo local __builtins__- esse é o motivo da discrepância entre duas dessas respostas. Observe também que __builtin__foi renomeado para builtinsem python3.

Curt Hagenlocher
fonte
2
Alguma razão para você não gostar dessa situação?
Software Enthusiastic
31
Por um lado, isso quebra as expectativas das pessoas quando elas estão lendo código. "O que esse símbolo 'foo' está sendo usado aqui? Por que não consigo ver onde está definido?"
Curt Hagenlocher
9
Também é suscetível a causar estragos se uma versão futura do Python começar a usar o nome que você escolheu como um built-in real.
intuited
4
Esta é uma boa solução para coisas como compartilhar uma conexão db com módulos importados. Como verificação de integridade, verifique se o módulo importado é confirmado hasattr(__builtin__, "foo").
perfil completo de Mike Ellis
4
Para quem está lendo esta resposta: NÃO! FAZ ! ESTA ! Realmente não.
Bruno desthuilliers
161

Se você precisar de uma variável global entre módulos, talvez apenas uma variável simples no nível de módulo seja suficiente.

a.py:

var = 1

b.py:

import a
print a.var
import c
print a.var

c.py:

import a
a.var = 2

Teste:

$ python b.py
# -> 1 2

Exemplo do mundo real: global_settings.py do Django (embora nas configurações dos aplicativos Django sejam usadas importando o objeto django.conf.settings ).

jfs
fonte
3
Melhor porque evita possíveis conflitos de namespace
BGW
E se o módulo que você está importando, neste caso a.py, contiver main()? Isso importa?
sedeh 22/08/14
4
@sedeh: não. Se o a.py também for executado como um script, use if __name__=="__main__"guard nele para evitar a execução de código inesperado na importação.
precisa
6
No mundo real, você precisa ter um pouco de cuidado com esta solução. Se um programador pegar sua variável 'global' usando 'de um var de importação' (tente esta variação em c.py), ele obterá uma cópia da variável no momento da importação.
Paul Whipp
1
@PaulWhipp: errado (dica: use id()para verificar a identidade)
JFS
25

Acredito que há muitas circunstâncias em que isso faz sentido e simplifica a programação para que alguns globais sejam conhecidos em vários módulos (fortemente acoplados). Nesse espírito, gostaria de elaborar um pouco a idéia de ter um módulo de globais importado pelos módulos que precisam fazer referência a eles.

Quando existe apenas um desses módulos, chamo-o de "g". Nele, atribuo valores padrão para cada variável que pretendo tratar como global. Em cada módulo que usa qualquer um deles, eu não uso "from g import var", pois isso resulta apenas em uma variável local que é inicializada a partir de g somente no momento da importação. Eu faço a maioria das referências na forma g.var e "g". serve como um lembrete constante de que estou lidando com uma variável potencialmente acessível a outros módulos.

Se o valor de uma variável global desse tipo for usado com freqüência em alguma função de um módulo, essa função poderá fazer uma cópia local: var = g.var. No entanto, é importante perceber que as atribuições para var são locais e o g.var global não pode ser atualizado sem fazer referência explicitamente ao g.var em uma atribuição.

Observe que você também pode ter vários módulos globais compartilhados por diferentes subconjuntos de seus módulos para manter as coisas um pouco mais controladas. O motivo de eu usar nomes abreviados para meus módulos globais é evitar sobrecarregar muito o código com as ocorrências deles. Com apenas um pouco de experiência, eles se tornam mnemônicos o suficiente com apenas 1 ou 2 caracteres.

Ainda é possível fazer uma atribuição para, digamos, gx quando x já não foi definido em g, e um módulo diferente pode acessar gx No entanto, mesmo que o intérprete permita, essa abordagem não é tão transparente e evito isto. Ainda existe a possibilidade de criar acidentalmente uma nova variável em g como resultado de um erro de digitação no nome da variável para uma atribuição. Às vezes, um exame de dir (g) é útil para descobrir nomes de surpresa que possam ter surgido por esse acidente.

David Vanderschel
fonte
7
Essa observação interessante resolveu meu problema: 'Eu não uso "from g import var", pois isso resulta apenas em uma variável local que é inicializada a partir de g somente no momento da importação. " Parece razoável supor que "from..import" seja o mesmo que "import", mas isso não é verdade.
Curtis Yallop
24

Defina um módulo (chame-o de "globalbaz") e tenha as variáveis ​​definidas dentro dele. Todos os módulos que usam esse "pseudoglobal" devem importar o módulo "globalbaz" e fazer referência a ele usando "globalbaz.var_name"

Isso funciona independentemente do local da alteração, você pode alterar a variável antes ou depois da importação. O módulo importado usará o valor mais recente. (Eu testei isso em um exemplo de brinquedo)

Para esclarecimento, o globalbaz.py é assim:

var_name = "my_useful_string"
hayalci
fonte
9

Você pode passar as globais de um módulo para outro:

No módulo A:

import module_b
my_var=2
module_b.do_something_with_my_globals(globals())
print my_var

No módulo B:

def do_something_with_my_globals(glob): # glob is simply a dict.
    glob["my_var"]=3
user394430
fonte
7

Variáveis ​​globais geralmente são uma má ideia, mas você pode fazer isso atribuindo a __builtins__:

__builtins__.foo = 'something'
print foo

Além disso, os próprios módulos são variáveis ​​que você pode acessar de qualquer módulo. Portanto, se você definir um módulo chamado my_globals.py:

# my_globals.py
foo = 'something'

Então você pode usar isso de qualquer lugar também:

import my_globals
print my_globals.foo

Usar módulos em vez de modificar __builtins__geralmente é uma maneira mais limpa de fazer globals desse tipo.

spiv
fonte
3
__builtins__é uma peculiaridade CPython, você realmente não deve usá-lo - melhor utilização __builtin__(ou builtinsem Python3) como a resposta aceita espectáculos
Tobias KIENZLER
5

Você já pode fazer isso com variáveis ​​no nível do módulo. Os módulos são os mesmos, independentemente do módulo do qual estão sendo importados. Assim, você pode tornar a variável uma variável no nível do módulo em qualquer módulo que faça sentido colocá-la e acessá-la ou atribuí-la a partir de outros módulos. Seria melhor chamar uma função para definir o valor da variável ou torná-la uma propriedade de algum objeto singleton. Dessa forma, se você precisar executar algum código quando a variável for alterada, poderá fazê-lo sem interromper a interface externa do seu módulo.

Normalmente, não é uma ótima maneira de fazer as coisas - usar globals raramente é - mas acho que essa é a maneira mais limpa de fazer isso.

intuído
fonte
3

Eu queria postar uma resposta dizendo que há um caso em que a variável não será encontrada.

As importações cíclicas podem interromper o comportamento do módulo.

Por exemplo:

first.py

import second
var = 1

second.py

import first
print(first.var)  # will throw an error because the order of execution happens before var gets declared.

main.py

import first

Neste exemplo, deve ser óbvio, mas em uma grande base de código, isso pode ser realmente confuso.

Jonathan
fonte
1

Isso parece modificar o __builtin__espaço para nome. Para fazer isso:

import __builtin__
__builtin__.foo = 'some-value'

Não use o __builtins__diretamente (observe os "s" extras)) - aparentemente, este pode ser um dicionário ou um módulo. Obrigado a ΤΖΩΤΖΙΟΥ por apontar isso, mais pode ser encontrado aqui .

Agora fooestá disponível para uso em qualquer lugar.

Eu não recomendo fazer isso geralmente, mas o uso disso depende do programador.

A atribuição a ele deve ser feita como acima, apenas a configuração foo = 'some-other-value'o definirá no espaço de nomes atual.

awatts
fonte
1
Lembro-me (do comp.lang.python) que o uso de builtins diretamente deve ser evitado; em vez disso, a importação embutido eo uso que, como Curt Hagenlocher sugeriu.
tzot 27/09/08
1

Eu uso isso para algumas funções primitivas internas que eu senti que estavam realmente faltando. Um exemplo é uma função find que possui a mesma semântica de uso que filter, map, red.

def builtin_find(f, x, d=None):
    for i in x:
        if f(i):
            return i
    return d

import __builtin__
__builtin__.find = builtin_find

Depois que isso é executado (por exemplo, importando próximo ao seu ponto de entrada), todos os seus módulos podem usar find () como se, obviamente, ele estivesse embutido.

find(lambda i: i < 0, [1, 3, 0, -5, -10])  # Yields -5, the first negative.

Nota: Você pode fazer isso, é claro, com filtro e outra linha para testar o comprimento zero ou reduzir em um tipo de linha estranha, mas sempre achei que era estranho.

Brian Arsuaga
fonte
1

Eu poderia obter variáveis modificáveis ​​(ou mutáveis ) entre módulos usando um dicionário:

# in myapp.__init__
Timeouts = {} # cross-modules global mutable variables for testing purpose
Timeouts['WAIT_APP_UP_IN_SECONDS'] = 60

# in myapp.mod1
from myapp import Timeouts

def wait_app_up(project_name, port):
    # wait for app until Timeouts['WAIT_APP_UP_IN_SECONDS']
    # ...

# in myapp.test.test_mod1
from myapp import Timeouts

def test_wait_app_up_fail(self):
    timeout_bak = Timeouts['WAIT_APP_UP_IN_SECONDS']
    Timeouts['WAIT_APP_UP_IN_SECONDS'] = 3
    with self.assertRaises(hlp.TimeoutException) as cm:
        wait_app_up(PROJECT_NAME, PROJECT_PORT)
    self.assertEqual("Timeout while waiting for App to start", str(cm.exception))
    Timeouts['WAIT_JENKINS_UP_TIMEOUT_IN_SECONDS'] = timeout_bak

Ao iniciar test_wait_app_up_fail, a duração real do tempo limite é de 3 segundos.

foudfou
fonte
1

Perguntei-me se seria possível evitar algumas das desvantagens do uso de variáveis ​​globais (veja, por exemplo, http://wiki.c2.com/?GlobalVariablesAreBad ) usando um espaço de nome de classe em vez de um espaço de nome global / módulo para passar valores de variáveis . O código a seguir indica que os dois métodos são essencialmente idênticos. Há uma pequena vantagem no uso de namespaces de classe, conforme explicado abaixo.

Os fragmentos de código a seguir também mostram que atributos ou variáveis ​​podem ser criados e excluídos dinamicamente nos espaços de nomes globais / de módulo e de classe.

wall.py

# Note no definition of global variables

class router:
    """ Empty class """

Eu chamo esse módulo de 'parede', pois ele é usado para devolver variáveis. Ele atuará como um espaço para definir temporariamente variáveis ​​globais e atributos de toda a classe do 'roteador' da classe vazia.

source.py

import wall
def sourcefn():
    msg = 'Hello world!'
    wall.msg = msg
    wall.router.msg = msg

Este módulo importa parede e define uma única função sourcefnque define uma mensagem e a emite por dois mecanismos diferentes, um via global e outro via função de roteador. Observe que as variáveis wall.msge wall.router.messagesão definidas aqui pela primeira vez em seus respectivos namespaces.

dest.py

import wall
def destfn():

    if hasattr(wall, 'msg'):
        print 'global: ' + wall.msg
        del wall.msg
    else:
        print 'global: ' + 'no message'

    if hasattr(wall.router, 'msg'):
        print 'router: ' + wall.router.msg
        del wall.router.msg
    else:
        print 'router: ' + 'no message'

Este módulo define uma função destfnque usa os dois mecanismos diferentes para receber as mensagens emitidas pela fonte. Permite a possibilidade de a variável 'msg' não existir.destfntambém exclui as variáveis ​​depois que elas são exibidas.

main.py

import source, dest

source.sourcefn()

dest.destfn() # variables deleted after this call
dest.destfn()

Este módulo chama as funções definidas anteriormente em sequência. Após a primeira chamada para dest.destfnas variáveis wall.msge wall.router.msgnão existe mais.

A saída do programa é:

global: Olá mundo!
roteador: Olá mundo!
global: nenhuma mensagem
roteador: nenhuma mensagem

Os fragmentos de código acima mostram que os mecanismos de módulo / global e de variável de classe / classe são essencialmente idênticos.

Se muitas variáveis ​​devem ser compartilhadas, a poluição do namespace pode ser gerenciada usando vários módulos do tipo parede, por exemplo, wall1, wall2 etc. ou definindo várias classes do tipo roteador em um único arquivo. O último é um pouco mais organizado, então talvez represente uma vantagem marginal para o uso do mecanismo de variável de classe.

robertofbaycot
fonte