2009-09-03 3 views
177

Как выполнить переменные переменные в Python?Как создать переменное число переменных?

Вот пояснительный ручной ввод, например: Variable variables

Я слышал, что это плохая идея, в общем, хотя, и это отверстие безопасности в Python. Это правда?

+16

это охранение и отладочные аспекты, которые вызывают ужас. Представьте, что вы пытаетесь выяснить, где изменилось переменное «foo», когда в вашем коде нет места, где вы действительно меняете «foo». Представьте себе, что это чужой код, который вам нужно поддерживать ... Хорошо, теперь вы можете отправиться в свое счастливое место. –

+3

Однако потребность все еще возникает. Раньше я думал, что мне нужно делать такие вещи все время, прежде чем я встречу реальные языки программирования. Отличные предложения здесь для перехода к более здравому мышлению. –

+0

Вы можете сделать это в макрос SAS. Иногда вам нужно, потому что единственным типом данных в макросах SAS является строка: -/ –

ответ

153

Вы можете использовать словари для этого. Словари - это хранилища ключей и ценностей.

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

Вы можете использовать имена переменных ключей для достижения эффекта переменных переменных без риска для безопасности.

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

В тех случаях, когда вы думаете делать что-то вроде

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

список может быть более подходящим, чем Dict. Список представляет собой упорядоченную последовательность объектов, с целыми индексами:

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

Для упорядоченных последовательностей, списки более удобны, чем dicts с целыми ключами, поскольку поддержка списков итерации в порядке индекса, slicing, append, и другие операции, потребует неловкого управления ключами с помощью dict.

+2

Боюсь, я не слишком хорошо понимаю. Вы можете объяснить? – 2009-09-03 12:42:10

+0

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

+0

Отредактировал свой ответ, чтобы объяснить словари. – 2009-09-03 12:49:15

26

Всякий раз, когда вы хотите использовать переменные переменные, вероятно, лучше использовать словарь. Таким образом, вместо того, чтобы писать

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

вы пишете

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

Таким образом, вы случайно не перезаписать ранее существующие переменные (это аспект безопасности), и вы можете иметь различные «пространства имен».

54

Используйте встроенную функцию getattr, чтобы получить атрибут объекта по имени. Измените имя по мере необходимости.

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

Это отлично работает с namedtuple – kap

+0

Lovely. Это именно то, что мне нужно. У меня есть объект и вам нужно получить доступ к переменным с помощью строки, и я не хотел использовать 'eval'. Благодаря! – rayryeng

49

Это не очень хорошая идея. Если вы обращаетесь к глобальной переменной, вы можете использовать globals().

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

Если вы хотите получить доступ к переменной в локальной области видимости, вы можете использовать locals(), но вы не можете присвоить значения возвращенного Dict.

Лучшее решение должно использовать getattr или хранить ваши переменные в словаре, а затем обращаться к ним по имени.

+11

+1 ответ на вопрос, как указано (хотя это ужасно) – bobince

+7

Не забудьте упомянуть, что вы не можете изменять переменные через locals() (http://docs.python.org/library/functions.html#locals). –

+0

Вы не можете установить переменные таким образом. Вы не можете делать 'globals() ['a'] = 10'. – sudo

1

Консенсус заключается в использовании словаря для этого - см. Другие ответы. Это хорошая идея для большинства случаев, однако есть много аспектов, связанных с этим:

  • Вы сами будете нести ответственность за этот словарь, включая сбор мусора (в переменных) и т. Д.
  • нет либо не локальности или глобальности для переменных величин, это зависит от глобальности словаря
  • если вы хотите переименовать имя переменной, вы должны сделать это вручную
  • однако, вы гораздо больше гибкий, например
    • вы можете решить перезаписать существующие переменные или ...
    • ... выбрать для реализации константные переменные
    • поднять исключение на перезапись для различных типов
    • и т.д.

Тем не менее, я внедрил класс variable variables manager, который содержит некоторые из вышеперечисленных идей. Она работает на Python 2 и 3.

Вы бы использовать the class так:

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

Если вы хотите разрешить перезапись переменных с тем же типом только:

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

На первый взгляд длинный верблюжонный импорт заставил меня подумать, что это была Java. – markroxor

6

У вас есть использовать globals() built in method для достижения такого поведения:

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

Вместо того, чтобы словарь, вы также можете использовать namedtuple из модуля коллекций, что облегчает доступ.

, например:

#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

Новые кодеры иногда писать код так:

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 (м * n), где m - количество названных переменных и n - это количество времени s, к которым необходимо получить доступ к группе переменных (включая создание). Более проницательный новичок отмечает, что единственная разница в каждой из этих строк - это число, которое изменяется на основе правила и решает использовать цикл. Тем не менее, они застревают на том, как динамически создавать эти имена переменных, а может попробовать что-то вроде этого:

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

Вскоре они обнаружили, что это не работает.

Если программе требуются произвольные переменные «имена», лучшим вариантом является словарь, как объясняется в других ответах. Однако, если вы просто пытаетесь создать много переменных, и вы не против ссылаться на них с последовательностью целых чисел, вы, вероятно, ищете list. Это особенно верно, если ваши данные являются однородными, например, ежедневные показания температуры, еженедельные оценки викторины или сетка графических виджетов.

Это может быть собрана следующим образом:

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

Это list также могут быть созданы в одной линии с понимания:

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

Результат в любом случае является заселена list, с первый элемент, к которому обратился my_calculator.buttons[0], следующий с my_calculator.buttons[1] и т. д. Имя переменной «base» становится именем list, и для доступа к нему используется различный идентификатор.

Наконец, не забудьте другие структуры данных, такие как set - это похоже на словарь, за исключением того, что каждое «имя» не имеет привязанного к нему значения. Если вам просто нужна «сумка» объектов, это может быть отличным выбором. Вместо того, чтобы что-то вроде этого:

keyword_1 = 'apple' 
keyword_2 = 'banana' 

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

Вы будете иметь это:

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

Используйте list для последовательности подобных объектов, а set для произвольно упорядоченного мешка объектов или dict для мешок с именами со связанными значениями.

1

Я отвечаю на вопрос: How to get the value of a variable given its name in a string? который закрыт как дубликат со ссылкой на этот вопрос.

Если переменные в вопросе являются частью объекта (часть класса, например), то некоторые полезные функции для достижения точности, которые являются hasattr, getattr и setattr.

Так, например, вы можете иметь:

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) 

Тогда вы можете сделать:

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

"initial_variable"

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

"на самом деле не является начальным"

1

Любой набор переменных также может быть обернут в классе. Переменные «Variable» могут быть добавлены к экземпляру класса во время выполнения, напрямую обращаясь к встроенному словарю через атрибут __dict__.

Следующий код определяет класс Variables, который добавляет переменные (в этом случае атрибуты) к своему экземпляру во время построения. Имена переменные взяты из указанного списка (который, например, может быть сгенерирован с помощью программного кода):

# 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

SimpleNamespace класса может быть использован для создания новых атрибутов с setattr или подклассом SimpleNamespace и создать свой собственный чтобы добавить новые имена атрибутов (переменные).

from types import SimpleNamespace 

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

Если вы не хотите использовать какой-либо объект, вы все еще можете использовать setattr() внутри текущего модуля:

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

Это звучит лучше для меня, чем при использовании «exec». – fralau

+0

Однако это не работает с переменной '__dict__'. Интересно, существует ли общий механизм для динамической * любой * глобальной переменной. – Alexey

+0

'globals()' может сделать это –

Смежные вопросы