¿Cómo creo un número variable de variables?

364

¿Cómo realizo variables variables en Python?

Aquí hay una entrada manual elaborada, por ejemplo: Variables variables

Sin embargo, he escuchado que esta es una mala idea en general, y es un agujero de seguridad en Python. ¿Es eso cierto?

Taryn
fuente
39
Son los aspectos de mantenimiento y depuración los que causan el horror. Imagínese tratando de averiguar dónde cambió la variable 'foo' cuando no hay un lugar en su código donde realmente cambie 'foo'. Imagine además que es el código de otra persona lo que debe mantener ... Bien, puede ir a su lugar feliz ahora.
Glenn Jackman
44
Otro escollo que no se ha mencionado hasta ahora es si una variable creada dinámicamente tiene el mismo nombre que una variable utilizada en su lógica. Básicamente, abre su software como rehén a la entrada que se le da.
holdenweb
Todas las respuestas aquí suponen que tiene acceso a las variables base a las que desea acceder dinámicamente por nombre, lo cual no siempre es el caso. Creo que el enfoque más general para reproducir el comportamiento de ejemplo en PHP es utilizar eval () así: var_name = 'foo'; bar = 5; output = eval (var_name)
Luis Vazquez
1
Puede modificar sus variables globales y locales accediendo a los diccionarios subyacentes para ellas; es una idea horrible desde una perspectiva de mantenimiento ... pero se puede hacer a través de globals (). update () y locals (). update () (o guardando la referencia dict de cualquiera de ellos y usándola como cualquier otro diccionario ) NO RECOMENDADO ... pero debes saber que es posible.
Jim Dennis
(Estoy agregando este comentario porque una pregunta similar, más específicamente solicitando este tipo de manipulación se cerró con un puntero a esta pregunta "lo suficientemente cercana". Quiero que los que siguen la pregunta cerrada obtengan su respuesta).
Jim Dennis

Respuestas:

297

Puede usar diccionarios para lograr esto. Los diccionarios son almacenes de claves y valores.

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

Puede usar nombres clave variables para lograr el efecto de variables variables sin el riesgo de seguridad.

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

Para casos en los que estás pensando en hacer algo como

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

una lista puede ser más apropiada que un dict. Una lista representa una secuencia ordenada de objetos, con índices enteros:

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 secuencias ordenadas, listas son más convenientes que predice con claves enteras, porque las listas apoyan iteración con el fin índice, rebanar , appendy otras operaciones que requiera la gestión de claves torpe con un diccionario.

ggorlen
fuente
88

Use la getattrfunción incorporada para obtener un atributo en un objeto por su nombre. Modifique el nombre según sea necesario.

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

No es una buena idea. Si está accediendo a una variable global que puede usar globals().

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

Si desea acceder a una variable en el ámbito local, puede usarla locals(), pero no puede asignar valores al dict devuelto.

Una mejor solución es usar getattro almacenar sus variables en un diccionario y luego acceder a ellas por su nombre.

Nadia Alramli
fuente
locals (). update ({'new_local_var': 'some local value'}) funciona bien para mí en Python 3.7.6; así que no estoy seguro de lo que quieres decir cuando dices que no puedes asignar valores a través de él.
Jim Dennis
Dado x = "foo"y locals()["x"] = "bar"usando print xda la salida barpara Jython 2.5.2. Esto se probó con un script de automatización bajo demanda en maximo .
Predicador
37

Siempre que quiera usar variables variables, probablemente sea mejor usar un diccionario. Entonces en lugar de escribir

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

usted escribe

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

De esta forma, no sobrescribirá accidentalmente variables previamente existentes (que es el aspecto de seguridad) y puede tener diferentes "espacios de nombres".

sepp2k
fuente
34

Los nuevos codificadores a veces escriben código como este:

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)
...

Luego, el codificador se queda con un montón de variables con nombre, con un esfuerzo de codificación de O ( m * n ), donde m es el número de variables con nombre yn es el número de veces que se necesita acceder a ese grupo de variables (incluida la creación ) El principiante más astuto observa que la única diferencia en cada una de esas líneas es un número que cambia según una regla, y decide usar un bucle. Sin embargo, se atascan en cómo crear dinámicamente esos nombres de variables y pueden intentar algo como esto:

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

Pronto descubren que esto no funciona.

Si el programa requiere "nombres" de variables arbitrarias, un diccionario es la mejor opción, como se explica en otras respuestas. Sin embargo, si simplemente está tratando de crear muchas variables y no le importa referirse a ellas con una secuencia de enteros, probablemente esté buscando unlist . Esto es particularmente cierto si sus datos son homogéneos, como lecturas diarias de temperatura, puntajes de cuestionarios semanales o una cuadrícula de widgets gráficos.

Esto se puede ensamblar de la siguiente manera:

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

Esto listtambién se puede crear en una línea con una comprensión:

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

El resultado en cualquier caso es una población list, con el primer elemento al que se accede my_calculator.buttons[0], el siguiente con my_calculator.buttons[1], y así sucesivamente. El nombre de la variable "base" se convierte en el nombre dellist identificador variable y se utiliza para acceder a él.

Finalmente, no olvide otras estructuras de datos, como el set- esto es similar a un diccionario, excepto que cada "nombre" no tiene un valor adjunto. Si simplemente necesita una "bolsa" de objetos, esta puede ser una excelente opción. En lugar de algo como esto:

keyword_1 = 'apple'
keyword_2 = 'banana'

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

Tendrás esto:

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

Use a listpara una secuencia de objetos similares, a setpara una bolsa de objetos ordenada arbitrariamente o dictpara una bolsa de nombres con valores asociados.

TigerhawkT3
fuente
12

En lugar de un diccionario, también puedes usar namedtuple desde el módulo de colecciones, lo que facilita el acceso.

Por ejemplo:

# 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
fuente
10

Si no desea usar ningún objeto, aún puede usarlo setattr()dentro de su módulo actual:

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
fuente
Este me suena mejor que usar 'exec'.
fralau
Sin embargo, esto no funciona con __dict__variables. Me pregunto si existe un mecanismo general para crear dinámicamente cualquier variable global.
Alexey
globals()puede hacer esto
Guillaume Lebreton
9

La SimpleNamespaceclase podría usarse para crear nuevos atributos con setattro subclase SimpleNamespacey crear su propia función para agregar nuevos nombres de atributos (variables).

from types import SimpleNamespace

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

Estoy respondiendo la pregunta: ¿Cómo obtener el valor de una variable dado su nombre en una cadena? que se cierra como un duplicado con un enlace a esta pregunta.

Si las variables en cuestión son parte de un objeto (parte de una clase, por ejemplo), entonces algunas funciones útiles para lograr exactamente eso son hasattr, getattrysetattr .

Entonces, por ejemplo, puedes tener:

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)

Entonces puedes hacer:

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

"variable_inicial"

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

"en realidad no es inicial"

patapouf_ai
fuente
6

Utilizar globals()

En realidad, puede asignar variables al ámbito global de forma dinámica, por ejemplo, si desea 10 variables a las que se puede acceder en un ámbito global i_1, i_2... i_10:

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

Esto asignará 'a' a todas estas 10 variables, por supuesto, también puede cambiar el valor dinámicamente. Ahora se puede acceder a todas estas variables como cualquier otra variable declarada globalmente:

>>> i_5
'a'
Rocky Li
fuente
4

Debe usar el globals()método integrado para lograr ese comportamiento:

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
fuente
3

El consenso es usar un diccionario para esto; vea las otras respuestas. Esta es una buena idea para la mayoría de los casos, sin embargo, hay muchos aspectos que surgen de esto:

  • usted mismo será responsable de este diccionario, incluida la recolección de basura (de variables dentro del diccionario), etc.
  • no hay localidad o globalidad para variables variables, depende de la globalidad del diccionario
  • si desea cambiar el nombre de una variable, deberá hacerlo manualmente
  • sin embargo, eres mucho más flexible, por ejemplo
    • puede decidir sobrescribir las variables existentes o ...
    • ... elige implementar variables const
    • para plantear una excepción sobre la sobrescritura de diferentes tipos
    • etc.

Dicho esto, he implementado un administrador de variables variables -clase que proporciona algunas de las ideas anteriores. Funciona para python 2 y 3.

Usarías la clase así:

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'])

Si desea permitir la sobrescritura de variables con el mismo tipo solamente:

myVars = VariableVariablesManager(enforceSameTypeOnOverride = True)
myVars['test'] = 25
myVars['test'] = "Cat" # <- raises Exception (different type on overwriting)
DomTomCat
fuente
A primera vista, las largas importaciones camelised me hicieron pensar que esto era Java.
markroxor
3

He intentado ambos en Python 3.7.3, puedes usar globals () o 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'


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

Hzzkygcs
fuente
0

Cualquier conjunto de variables también se puede agrupar en una clase. Las variables "variables" se pueden agregar a la instancia de clase durante el tiempo de ejecución accediendo directamente al diccionario integrado a través del atributo __dict__.

El siguiente código define la clase Variables, que agrega variables (en este caso, atributos) a su instancia durante la construcción. Los nombres de las variables se toman de una lista específica (que, por ejemplo, podría haber sido generada por el código del 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
fuente