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


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.


88

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

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

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"

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


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для пакета имен с соответствующими значениями.


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

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


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)

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


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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.