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

364

Как мне сделать переменные переменные в Python?

Вот подробное руководство, например: Переменные переменные

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

Тарын
источник
39
это аспекты обслуживания и отладки, которые вызывают ужас. Представьте себе, что вы пытаетесь узнать, где изменилась переменная 'foo', когда в вашем коде нет места, где вы на самом деле меняете 'foo'. Представьте себе, что вам нужно сохранить чужой код ... Хорошо, теперь вы можете отправиться в свое счастливое место.
Гленн Джекман
4
Еще один подводный камень, о котором до сих пор не говорилось, - это если такая динамически создаваемая переменная имеет то же имя, что и переменная, используемая в вашей логике. По сути, вы открываете свое программное обеспечение в качестве заложника того, что ему дается.
holdenweb
Все ответы здесь предполагают, что у вас есть доступ к базовым переменным, к которым вы хотите получить динамический доступ по имени, что не всегда так. Я думаю, что наиболее общий подход к воспроизведению примера поведения в PHP заключается в использовании eval () следующим образом: var_name = 'foo'; бар = 5; output = eval (var_name)
Луис Васкес
1
Вы можете изменить свои глобальные и локальные переменные, обратившись к базовым словарям для них; это ужасная идея с точки зрения обслуживания ... но это может быть сделано с помощью globals (). update () и locals (). update () (или путем сохранения ссылки на dict из любого из них и использования ее как любого другого словаря ). НЕ РЕКОМЕНДУЕТСЯ ... но вы должны знать, что это возможно.
Джим Деннис
(Я добавляю этот комментарий, потому что похожий вопрос, более конкретно запрашивающий такого рода манипуляции, был закрыт указателем на этот «достаточно близкий» вопрос. Я хочу, чтобы те, кто следует за закрытым вопросом, получили свой ответ).
Джим Деннис

Ответы:

297

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

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

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

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

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

ggorlen
источник
88

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

obj.spam = 'eggs'
name = 'spam'
getattr(obj, name)  # returns 'eggs'
SilentGhost
источник
70

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

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

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

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

Надя Алрамли
источник
locals (). update ({'new_local_var': 'some local value'}) прекрасно работает для меня в Python 3.7.6; поэтому я не уверен, что вы имеете в виду, когда говорите, что не можете назначать значения через него.
Джим Деннис
Учитывая x = "foo"и locals()["x"] = "bar"используя print xдает выход barдля Jython 2.5.2. Это было проверено с помощью скрипта автоматизации по требованию в Maximo .
Проповедник
37

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

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

ты пишешь

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

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

sepp2k
источник
34

Новые кодеры иногда пишут такой код:

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

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

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

Если программе требуются произвольные переменные «имена», лучшим выбором будет словарь, как объяснено в других ответах. Однако, если вы просто пытаетесь создать много переменных и не возражаете обращаться к ним с помощью последовательности целых чисел, вы, вероятно, ищете a 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]и так далее. «Базовое» имя переменной становится именем, 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.')

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

TigerhawkT3
источник
12

Вместо словаря вы также можете использовать его 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)
Охас Морриль
источник
10

Если вы не хотите использовать какой-либо объект, вы все равно можете использовать его 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
Гийом Лебретон
источник
Это звучит лучше для меня, чем использование exec.
Fralau
Это не работает с __dict__переменной однако. Интересно, существует ли общий механизм для динамического создания любой глобальной переменной.
Алексей
globals()может сделать это
Гийом Лебретон
9

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

from types import SimpleNamespace

variables = {"b":"B","c":"C"}
a = SimpleNamespace(**variables)
setattr(a,"g","G")
a.g = "G+"
something = a.a
Билл Олдройд
источник
6

Я отвечаю на вопрос: как получить значение переменной, учитывая ее имя в строке? который закрыт как дубликат со ссылкой на этот вопрос.

Если переменные в вопросе являются частью объекта (часть класса, например) , то некоторые полезные функции для достижения точности , которые являются 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

"на самом деле не начальный"

patapouf_ai
источник
6

использование globals()

Вы можете фактически назначать переменные глобальной области видимости динамически, например, если вы хотите, чтобы 10 переменных были доступны в глобальной области видимости i_1, i_2... i_10:

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

Это назначит 'a' всем этим 10 переменным, конечно, вы можете также динамически изменять значение. Все эти переменные теперь доступны, как и другие глобально объявленные переменные:

>>> i_5
'a'
Роки Ли
источник
4

Вы должны использовать globals()встроенный метод для достижения такого поведения:

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
Андрей Иванейко
источник
3

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

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

Тем не менее, я реализовал класс переменных переменных manager -class, который предоставляет некоторые из приведенных выше идей. Это работает для Python 2 и 3.

Вы бы использовали класс следующим образом:

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)
DomTomCat
источник
На первый взгляд долгий импорт верблюдов заставил меня думать, что это Java.
Маркроксор
3

Я пробовал оба в Python 3.7.3, вы можете использовать либо globals () или 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'


Ссылка:
https://www.daniweb.com/programming/software-development/threads/111526/setting-a-string-as-a-variable-name#post548936

Hzzkygcs
источник
0

Любой набор переменных также может быть помещен в класс. «Переменные» переменные могут быть добавлены к экземпляру класса во время выполнения путем прямого доступа к встроенному словарю через атрибут __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
ru13r
источник