Як створити змінну кількість змінних?


363

Як виконати змінні змінні в Python?

Ось докладний ручний запис, наприклад: Змінні змінні

Я чув, що це взагалі погана ідея, і це отвір у безпеці в Python. Це правда?


39
саме жахливість та аспекти налагодження викликають жах. Уявіть, що ви намагаєтеся дізнатись, де змінювалася змінна "foo", коли у вашому коді немає місця, де ви фактично змінюєте "foo". Далі уявіть, що це чийсь код, який вам доведеться підтримувати ... Добре, зараз можете відправитися до свого щасливого місця.
glenn jackman

4
Подальший підводний камінь, про який досі не згадувалося, полягає в тому, якщо така динамічно створена змінна має те саме ім'я, що і змінна, що використовується у вашій логіці. Ви по суті відкриваєте своє програмне забезпечення як заручника вкладеного ним вкладу.
holdenweb

Усі відповіді тут передбачають, що ви маєте доступ до базових змінних, до яких хочете динамічно отримати доступ по імені, що не завжди так. Я думаю, що найбільш загальним підходом до відтворення прикладу поведінки в PHP є використання eval () на зразок цього: var_name = 'foo'; бар = 5; вихід = eval (var_name)
Луїс Васкес

1
Ви можете змінювати свої глобальні та локальні змінні, отримуючи доступ до основних словників для них; це жахлива ідея з точки зору технічного обслуговування ... але це можна зробити за допомогою глобалів (). update () та localals (). update () (або збереження посилання на дік з будь-якого з них та використання його, як і будь-якого іншого словника ). НЕ РЕКОМЕНДОВАНО ... але ви повинні знати, що це можливо.
Джим Денніс

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

Відповіді:


296

Для цього можна використовувати словники . Словники - це сховища ключів та значень.

>>> 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(), але ви не можете призначити значення поверненому диктату.

Краще рішення - використовувати getattrабо зберігати змінні у словнику, а потім отримувати доступ до них по імені.


localals (). update ({'new_local_var': 'деяке місцеве значення'}) для мене працює чудово в 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)

Незабаром вони виявляють, що це не працює.

Якщо програма вимагає довільної змінної "імена", найкращим вибором є словник, як пояснено в інших відповідях. Однак якщо ви просто намагаєтеся створити багато змінних і не проти посилатися на них з послідовністю цілих чисел, ви, ймовірно, шукаєте 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.')

Використовуйте 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")

"початковий_перемінний"

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

"насправді не є початковим"


5

Використовуйте 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

Консенсус полягає у використанні для цього словника - дивіться інші відповіді. Це гарна ідея для більшості випадків, проте з цього виникає багато аспектів:

  • ви самі несете відповідальність за цей словник, включаючи збирання сміття (з нерозбірливих змінних) тощо.
  • немає або локальності, або глобальності для змінних змінних, це залежить від глобальності словника
  • якщо ви хочете перейменувати ім’я змінної, вам доведеться це зробити вручну
  • однак ви набагато гнучкіші, наприклад
    • ви можете вирішити замінити існуючі змінні або ...
    • ... вибрати реалізацію змінних const
    • створити виняток при перезаписі для різних типів
    • тощо.

З цього приводу я реалізував менеджер змінних змінних -класс, який надає деякі з перерахованих вище ідей. Він працює для пітонів 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)

З першого погляду довгий імпорт з верблюдів змусив мене подумати, що це Ява.
markroxor

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

Наступний код визначає клас змінних, який додає змінні (у цьому випадку атрибути) до його примірника під час побудови. Імена змінних взяті із визначеного списку (який, наприклад, міг бути створений програмним кодом):

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