2009-09-03 12 views
177

¿Cómo logro las variables variables en Python?¿Cómo creo una cantidad variable de variables?

Aquí se muestra una entrada manual de elaborative, por ejemplo: Variable variables

He oído que es una mala idea en general, sin embargo, y es un agujero de seguridad en Python. ¿Es eso cierto?

+16

se trata de los aspectos de comunidad y depuración que causan el horror. Imagine tratar de averiguar dónde cambió la variable 'foo' cuando no hay lugar en su código donde realmente cambie 'foo'. Imagine además que es el código de otra persona el que tiene que mantener ... OK, puede ir a su lugar feliz ahora. –

+3

La necesidad todavía surge, sin embargo. Solía ​​pensar que tenía que hacer este tipo de cosas todo el tiempo antes de encontrarme con lenguajes de programación reales. Grandes sugerencias aquí para la transición a una mentalidad más sana. –

+0

Puede hacerlo en la macrolanza SAS. A veces tiene que hacerlo, porque el único tipo de datos en macros SAS es la cadena: -/ –

Respuesta

153

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 de teclas variables para lograr el efecto de variables variables sin el riesgo de seguridad.

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

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

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

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

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

Para secuencias ordenadas, las listas son más convenientes que dicts con teclas de número entero, porque las listas apoyan iteración con el fin de índice, slicing, append, y otras operaciones que requeriría una gestión de clave incómoda con un dict.

+2

Me temo que no entiendo muy bien. ¿Puedes explicar? –

+0

http://docs.python.org/library/stdtypes.html#dict –

+0

Edité mi respuesta para explicar los diccionarios. –

26

Cuando quiera usar variables variables, probablemente sea mejor usar un diccionario. Así que en lugar de escribir

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

se escribe

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

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

54

Utilice la función incorporada getattr para obtener un atributo en un objeto por su nombre. Modificar el nombre según sea necesario.

obj.spam = 'eggs' 
name = 'spam' 
getattr(obj, name) # returns 'eggs' 
+0

Eso funciona muy bien con un namedtuple – kap

+0

Lovely. Esto es exactamente lo que necesitaba. Tengo un objeto y necesito acceder a las variables mediante una cadena y no quería usar 'eval'. ¡Gracias! – rayryeng

49

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

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

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

Una mejor solución es utilizargetattr o almacenar sus variables en un diccionario y luego acceder a ellos por su nombre.

+11

+1 respondiendo a la pregunta tal como se establece (aunque es horrible) – bobince

+7

No olvide mencionar que no puede modificar variables a través de locals() (http://docs.python.org/library/functions.html#locals) –

+0

No puede establecer variables de esta manera. No puedes hacer 'globals() ['a'] = 10'. – sudo

1

El consenso es utilizar un diccionario para esto - ver 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, incluyendo la recolección de basura (de variables en-dict), etc.
  • hay tampoco ninguna localidad o la globalidad de variables variables, depende de la globalidad del diccionario
  • si desea cambiar el nombre de un nombre de variable, que tendrá que hacerlo manualmente
  • sin embargo, son mucho más flexible, por ejemplo
    • usted puede decidir si desea sobrescribir las variables existentes o ...
    • ... optan por aplicar las variables const
    • a lanzar una excepción al sobrescribir para diferentes tipos
    • etc.

Dicho esto, he implementado una clase variable variables manager que proporciona algunas de las ideas anteriores. Funciona para Python 2 y 3.

que tendría que utilizar the class 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 sobreescritura de variables con el mismo tipo solamente:

myVars = VariableVariablesManager(enforceSameTypeOnOverride = True) 
myVars['test'] = 25 
myVars['test'] = "Cat" # <- raises Exception (different type on overwriting) 
+0

A primera vista, las importaciones largas camelised me hicieron pensar que esto era Java. – markroxor

6

Tienes globals() built in method utilizar para lograr que el 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 
8

en lugar de una diccionario también puede usar namedtuple del 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 
11

nuevos codificadores veces escribir 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) 
... 

El codificador se deja 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 y n es el número de veces s ese grupo de variables debe ser accedido (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 en función de una regla, y decide usar un ciclo. Sin embargo, se quedan atascados en cómo crear dinámicamente los nombres de las variables, y pueden intentar algo como esto:

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

Ellos 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 intenta crear muchas variables y no le importa hacer referencia a ellas con una secuencia de enteros, probablemente esté buscando un list. Esto es particularmente cierto si sus datos son homogéneos, como lecturas de temperatura diarias, puntajes semanales de prueba o una grilla de widgets gráficos.

Esto puede ser ensamblado como sigue:

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

Este list también se pueden crear en una sola línea con una comprensión:

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

El resultado en ambos casos es una poblada list, con el primer elemento al que se accede con 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 list y el identificador variable se utiliza para acceder a él.

Por último, no olvide otras estructuras de datos, como 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 gran opción. En lugar de algo como esto:

keyword_1 = 'apple' 
keyword_2 = 'banana' 

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

Usted tendrá la siguiente:

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

Utilice un list para una secuencia de objetos similares, una set para una bolsa ordenada arbitraria de objetos, o una dict de una bolsa de nombres con valores asociados.

1

Estoy respondiendo la pregunta: How to get the value of a variable given its name in a string? 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, getattr y setattr.

Así, por ejemplo, usted puede 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 usted puede hacer:

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

"initial_variable"

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

"no es en realidad inicial"

1

Cualquier conjunto de variables también se puede incluir en una clase. Las variables "variables" se pueden agregar a la instancia de clase durante el tiempo de ejecución accediendo directamente al diccionario incorporado 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 variables son tomadas de una lista especificada (que, por ejemplo, podría haber sido generado 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 
1

La clase SimpleNamespace podría ser utilizado para crear nuevos atributos con setattr, o subclase SimpleNamespace y crear su propio función para agregar nuevos nombres de atributos (variables).

from types import SimpleNamespace 

variables = {"b":"B","c":"C"} 
a = SimpleNamespace(**v) 
setattr(a,"g","G") 
a.g = "G+" 
something = a.a 
2

Si no desea utilizar cualquier objeto, todavía se puede utilizar 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 
+0

Éste me suena mejor que usar 'exec'. – fralau

+0

Sin embargo, esto no funciona con la variable '__dict__'. Me pregunto si existe un mecanismo general para crear * cualquier * variable global de forma dinámica. – Alexey

+0

'globals()' puede hacer esto –

Cuestiones relacionadas