Como crio um número variável de variáveis?

364

Como realizo variáveis ​​variáveis ​​em Python?

Aqui está uma entrada manual elaborativa, por exemplo: Variáveis ​​variáveis

Ouvi dizer que essa é uma má ideia em geral, e é uma falha de segurança no Python. Isso é verdade?

Taryn
fonte
39
são os aspectos de manutenção e depuração que causam o horror. Imagine tentar descobrir onde a variável 'foo' mudou quando não há lugar no seu código onde você realmente muda 'foo'. Imagine ainda que é o código de outra pessoa que você deve manter ... OK, você pode ir para o seu lugar feliz agora.
21139 glenn jackman
4
Outra armadilha que não foi mencionada até agora é se uma variável criada dinamicamente tiver o mesmo nome que uma variável usada em sua lógica. Você essencialmente abre o seu software como refém da informação fornecida.
precisa
Todas as respostas aqui assumem que você tem acesso às variáveis ​​básicas que deseja acessar dinamicamente por nome, o que nem sempre é o caso. Eu acho que a abordagem mais geral para reproduzir o comportamento de exemplo no PHP é usar eval () assim: var_name = 'foo'; bar = 5; output = eval (var_name)
Luis Vazquez
11
Você pode modificar suas variáveis ​​globais e locais acessando os dicionários subjacentes a elas; é uma ideia horrível do ponto de vista da manutenção ... mas pode ser feita via globals (). update () e locals (). update () (ou salvando a referência dict de qualquer uma delas e usando-a como qualquer outro dicionário) ) NÃO RECOMENDADO ... mas você deve saber que é possível.
Jim Dennis
(Estou adicionando este comentário porque uma pergunta semelhante, solicitando mais especificamente esse tipo de manipulação, foi encerrada com um ponteiro para esta pergunta "suficientemente próxima". Quero que os que seguem a pergunta fechada recebam a resposta).
Jim Dennis

Respostas:

297

Você pode usar dicionários para fazer isso. Dicionários são lojas de chaves e valores.

>>> dct = {'x': 1, 'y': 2, 'z': 3}
>>> dct
{'y': 2, 'x': 1, 'z': 3}
>>> dct["y"]
2

Você pode usar nomes de chaves variáveis ​​para obter o efeito de variáveis ​​variáveis ​​sem o risco à segurança.

>>> x = "spam"
>>> z = {x: "eggs"}
>>> z["spam"]
'eggs'

Nos casos em que você está pensando em fazer algo como

var1 = 'foo'
var2 = 'bar'
var3 = 'baz'
...

uma lista pode ser mais apropriada que um ditado. Uma lista representa uma sequência ordenada de objetos, com índices inteiros:

lst = ['foo', 'bar', 'baz']
print(lst[1])           # prints bar, because indices start at 0
lst.append('potatoes')  # lst is now ['foo', 'bar', 'baz', 'potatoes']

Para as seqüências ordenadas, listas são mais convenientes do que dicts com chaves inteiras, porque as listas apoiar iteração na ordem do índice, corte , appende outras operações que exigiria o gerenciamento de chaves desajeitado com um dicionário.

ggorlen
fonte
88

Use a getattrfunção interna para obter um atributo em um objeto pelo nome. Modifique o nome conforme necessário.

obj.spam = 'eggs'
name = 'spam'
getattr(obj, name)  # returns 'eggs'
SilentGhost
fonte
70

Não é uma boa ideia. Se você estiver acessando uma variável global, poderá usar globals().

>>> a = 10
>>> globals()['a']
10

Se você deseja acessar uma variável no escopo local, pode usar locals(), mas não pode atribuir valores ao ditado retornado.

Uma solução melhor é usar getattrou armazenar suas variáveis ​​em um dicionário e depois acessá-las pelo nome.

Nadia Alramli
fonte
locals (). update ({'new_local_var': 'some value local'}) funciona muito bem para mim no Python 3.7.6; portanto, não tenho certeza do que você quer dizer quando diz que não pode atribuir valores por meio dele.
Jim Dennis
Dado x = "foo"e locals()["x"] = "bar"usando print xfornece a saída barpara o Jython 2.5.2. Isso foi testado com um script de automação sob demanda no maximo .
Pregador
37

Sempre que você quiser usar variáveis ​​variáveis, provavelmente é melhor usar um dicionário. Então, ao invés de escrever

$foo = "bar"
$$foo = "baz"

você escreve

mydict = {}
foo = "bar"
mydict[foo] = "baz"

Dessa forma, você não substituirá acidentalmente variáveis ​​existentes anteriormente (que é o aspecto de segurança) e poderá ter "namespaces" diferentes.

sepp2k
fonte
34

Às vezes, novos codificadores escrevem código assim:

my_calculator.button_0 = tkinter.Button(root, text=0)
my_calculator.button_1 = tkinter.Button(root, text=1)
my_calculator.button_2 = tkinter.Button(root, text=2)
...

O codificador é então deixado com uma pilha de variáveis chamadas, com um esforço de codificação de O ( m * n ), onde m é o número de variáveis nomeadas e n é o número de vezes que o grupo de necessidades variáveis para ser acedido (incluindo criação ) O iniciante mais astuto observa que a única diferença em cada uma dessas linhas é um número que muda com base em uma regra e decide usar um loop. No entanto, eles ficam presos em como criar dinamicamente esses nomes de variáveis ​​e podem tentar algo como isto:

for i in range(10):
    my_calculator.('button_%d' % i) = tkinter.Button(root, text=i)

Eles logo descobrem que isso não funciona.

Se o programa exigir "nomes" variáveis ​​arbitrários, um dicionário é a melhor opção, conforme explicado em outras respostas. No entanto, se você está simplesmente tentando criar muitas variáveis ​​e não se importa de se referir a elas com uma sequência de números inteiros, provavelmente está procurando por list. Isso é particularmente verdadeiro se seus dados forem homogêneos, como leituras diárias de temperatura, pontuações semanais de questionários ou uma grade de widgets gráficos.

Isso pode ser montado da seguinte maneira:

my_calculator.buttons = []
for i in range(10):
    my_calculator.buttons.append(tkinter.Button(root, text=i))

Isso listtambém pode ser criado em uma linha com uma compreensão:

my_calculator.buttons = [tkinter.Button(root, text=i) for i in range(10)]

Em ambos os casos, o resultado é um preenchido list, com o primeiro elemento acessado com my_calculator.buttons[0], o próximo com my_calculator.buttons[1]e assim por diante. O nome da variável "base" torna-se o nome do liste o identificador variável é usado para acessá-lo.

Por fim, não esqueça de outras estruturas de dados, como o set- isto é semelhante a um dicionário, exceto que cada "nome" não tem um valor anexado a ele. Se você simplesmente precisa de uma "bolsa" de objetos, essa pode ser uma ótima opção. Em vez de algo como isto:

keyword_1 = 'apple'
keyword_2 = 'banana'

if query == keyword_1 or query == keyword_2:
    print('Match.')

Você terá isso:

keywords = {'apple', 'banana'}
if query in keywords:
    print('Match.')

Use a listpara uma sequência de objetos semelhantes, setpara uma bolsa de objetos ordenada arbitrariamente ou dictpara uma bolsa de nomes com valores associados.

TigerhawkT3
fonte
12

Em vez de um dicionário, você também pode usar namedtupleo módulo de coleções, o que facilita o acesso.

Por exemplo:

# using dictionary
variables = {}
variables["first"] = 34
variables["second"] = 45
print(variables["first"], variables["second"])

# using namedtuple
Variables = namedtuple('Variables', ['first', 'second'])
vars = Variables(34, 45)
print(vars.first, vars.second)
ojas mohril
fonte
10

Se você não quiser usar nenhum objeto, ainda poderá usá-lo setattr()dentro do seu módulo atual:

import sys
current_module = module = sys.modules[__name__]  # i.e the "file" where your code is written
setattr(current_module, 'variable_name', 15)  # 15 is the value you assign to the var
print(variable_name)  # >>> 15, created from a string
Guillaume Lebreton
fonte
Este soa melhor para mim do que usar 'exec'.
Fralau
Isso não funciona com a __dict__variável no entanto. Gostaria de saber se existe um mecanismo geral para criar qualquer variável global dinamicamente.
Alexey #
globals()pode fazer isso
Guillaume Lebreton 31/01
9

A SimpleNamespaceclasse pode ser usada para criar novos atributos com setattrou subclasse SimpleNamespacee criar sua própria função para adicionar novos nomes de atributos (variáveis).

from types import SimpleNamespace

variables = {"b":"B","c":"C"}
a = SimpleNamespace(**variables)
setattr(a,"g","G")
a.g = "G+"
something = a.a
Bill Oldroyd
fonte
6

Estou respondendo à pergunta: como obter o valor de uma variável com o nome em uma string? que é fechado como duplicado com um link para esta pergunta.

Se as variáveis em questão fazem parte de um objeto (parte de uma classe, por exemplo), em seguida, algumas funções úteis para conseguir exatamente o que são hasattr, getattre setattr.

Então, por exemplo, você pode ter:

class Variables(object):
    def __init__(self):
        self.foo = "initial_variable"
    def create_new_var(self,name,value):
        setattr(self,name,value)
    def get_var(self,name):
        if hasattr(self,name):
            return getattr(self,name)
        else:
            raise("Class does not have a variable named: "+name)

Então você pode fazer:

v = Variables()
v.get_var("foo")

"Variável_inicial"

v.create_new_var(v.foo,"is actually not initial")
v.initial_variable

"na verdade não é inicial"

patapouf_ai
fonte
5

Usar globals()

Você pode realmente atribuir variáveis ​​ao escopo global dinamicamente, por exemplo, se desejar 10 variáveis ​​que podem ser acessadas em um escopo global i_1, i_2... i_10:

for i in range(10):
    globals()['i_{}'.format(i)] = 'a'

Isso atribuirá 'a' a todas essas 10 variáveis, é claro que você também pode alterar o valor dinamicamente. Todas essas variáveis ​​podem ser acessadas agora como outra variável declarada globalmente:

>>> i_5
'a'
Rocky Li
fonte
4

Você precisa usar o globals()método interno para obter esse comportamento:

def var_of_var(k, v):
    globals()[k] = v

print variable_name # NameError: name 'variable_name' is not defined
some_name = 'variable_name'
globals()[some_name] = 123
print variable_name # 123

some_name = 'variable_name2'
var_of_var(some_name, 456)
print variable_name2 # 456
Andriy Ivaneyko
fonte
3

O consenso é usar um dicionário para isso - veja as outras respostas. Essa é uma boa ideia para a maioria dos casos, no entanto, existem muitos aspectos decorrentes disso:

  • você será responsável por este dicionário, incluindo coleta de lixo (de variáveis ​​dentro do dict) etc.
  • não há localidade ou globalidade para variáveis ​​variáveis, depende da globalidade do dicionário
  • se você quiser renomear um nome de variável, precisará fazê-lo manualmente
  • no entanto, você é muito mais flexível, por exemplo
    • você pode sobrescrever variáveis ​​existentes ou ...
    • ... optar por implementar variáveis ​​const
    • para gerar uma exceção na substituição de tipos diferentes
    • etc.

Dito isso, implementei uma classe- manager de variáveis ​​variáveis, que fornece algumas das idéias acima. Funciona para o python 2 e 3.

Você usaria a classe assim:

from variableVariablesManager import VariableVariablesManager

myVars = VariableVariablesManager()
myVars['test'] = 25
print(myVars['test'])

# define a const variable
myVars.defineConstVariable('myconst', 13)
try:
    myVars['myconst'] = 14 # <- this raises an error, since 'myconst' must not be changed
    print("not allowed")
except AttributeError as e:
    pass

# rename a variable
myVars.renameVariable('myconst', 'myconstOther')

# preserve locality
def testLocalVar():
    myVars = VariableVariablesManager()
    myVars['test'] = 13
    print("inside function myVars['test']:", myVars['test'])
testLocalVar()
print("outside function myVars['test']:", myVars['test'])

# define a global variable
myVars.defineGlobalVariable('globalVar', 12)
def testGlobalVar():
    myVars = VariableVariablesManager()
    print("inside function myVars['globalVar']:", myVars['globalVar'])
    myVars['globalVar'] = 13
    print("inside function myVars['globalVar'] (having been changed):", myVars['globalVar'])
testGlobalVar()
print("outside function myVars['globalVar']:", myVars['globalVar'])

Se você deseja permitir a substituição de variáveis ​​apenas do mesmo tipo:

myVars = VariableVariablesManager(enforceSameTypeOnOverride = True)
myVars['test'] = 25
myVars['test'] = "Cat" # <- raises Exception (different type on overwriting)
DomTomCat
fonte
À primeira vista, as longas importações camelizadas me fizeram pensar que era Java.
MarkRoxor # 19/18
3

Eu tentei tanto no python 3.7.3, você pode usar globals () ou vars ()

>>> food #Error
>>> milkshake #Error
>>> food="bread"
>>> drink="milkshake"
>>> globals()[food] = "strawberry flavor"
>>> vars()[drink] = "chocolate flavor"
>>> bread
'strawberry flavor'
>>> milkshake
'chocolate flavor'
>>> globals()[drink]
'chocolate flavor'
>>> vars()[food]
'strawberry flavor'


Referência:
https://www.daniweb.com/programming/software-development/threads/111526/setting-a-string-as-a-variable-name#post548936

Hzzkygcs
fonte
0

Qualquer conjunto de variáveis ​​também pode ser agrupado em uma classe. As variáveis ​​"Variáveis" podem ser adicionadas à instância da classe durante o tempo de execução, acessando diretamente o dicionário interno por meio do atributo __dict__.

O código a seguir define a classe Variables, que adiciona variáveis ​​(neste caso, atributos) à sua instância durante a construção. Os nomes de variáveis ​​são obtidos de uma lista especificada (que, por exemplo, poderia ter sido gerada pelo código do programa):

# some list of variable names
L = ['a', 'b', 'c']

class Variables:
    def __init__(self, L):
        for item in L:
            self.__dict__[item] = 100

v = Variables(L)
print(v.a, v.b, v.c)
#will produce 100 100 100
ru13r
fonte