Як виконати змінні змінні в Python?
Ось докладний ручний запис, наприклад: Змінні змінні
Я чув, що це взагалі погана ідея, і це отвір у безпеці в Python. Це правда?
Як виконати змінні змінні в Python?
Ось докладний ручний запис, наприклад: Змінні змінні
Я чув, що це взагалі погана ідея, і це отвір у безпеці в Python. Це правда?
Відповіді:
Для цього можна використовувати словники . Словники - це сховища ключів та значень.
>>> 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.
Використовуйте вбудовану getattr
функцію, щоб отримати атрибут на об'єкті по імені. Змініть ім'я за потребою.
obj.spam = 'eggs'
name = 'spam'
getattr(obj, name) # returns 'eggs'
Це не дуже гарна ідея. Якщо ви отримуєте доступ до глобальної змінної, ви можете використовувати globals()
.
>>> a = 10
>>> globals()['a']
10
Якщо ви хочете отримати доступ до змінної в локальній області, ви можете використовувати locals()
, але ви не можете призначити значення поверненому диктату.
Краще рішення - використовувати getattr
або зберігати змінні у словнику, а потім отримувати доступ до них по імені.
x = "foo"
та locals()["x"] = "bar"
використання print x
дає вихід bar
для Jython 2.5.2. Це було протестовано за допомогою сценарію автоматизації на вимогу в maximo .
Всякий раз, коли ви хочете використовувати змінні змінні, можливо, краще використовувати словник. Тож замість того, щоб писати
$foo = "bar"
$$foo = "baz"
Ви пишете
mydict = {}
foo = "bar"
mydict[foo] = "baz"
Таким чином, ви випадково не перезапишете раніше існуючі змінні (що є аспектом безпеки), і у вас можуть бути різні "простори імен".
Нові кодери іноді пишуть такий код:
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
для мішка з іменами з пов'язаними значеннями.
Замість словника ви також можете використовувати 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)
Якщо ви не хочете використовувати жоден об’єкт, ви все одно можете використовувати 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
__dict__
змінною. Цікаво, чи існує загальний механізм, щоб динамічно створювати будь-яку глобальну змінну.
globals()
може це зробити
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
Я відповідаю на питання: Як отримати значення змінної, давши її ім'я в рядку? яка закрита як дублікат із посиланням на це питання.
Якщо змінні в питанні є частиною об'єкта (частина класу, наприклад) , то деякі корисні функції для досягнення точності , які є 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
"насправді не є початковим"
Використовуйте globals()
Ви можете реально динамічно присвоювати змінні глобальній області, наприклад, якщо ви хочете 10 змінних, до яких можна отримати доступ у глобальній області i_1
, i_2
... i_10
:
for i in range(10):
globals()['i_{}'.format(i)] = 'a'
Це призначить "a" всім цим 10 змінним, звичайно, ви також можете змінити значення також динамічно. До всіх цих змінних можна отримати доступ, як і до іншої глобально оголошеної змінної:
>>> i_5
'a'
Для досягнення такої поведінки ви повинні використовувати 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
Консенсус полягає у використанні для цього словника - дивіться інші відповіді. Це гарна ідея для більшості випадків, проте з цього виникає багато аспектів:
З цього приводу я реалізував менеджер змінних змінних -класс, який надає деякі з перерахованих вище ідей. Він працює для пітонів 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)
Я спробував обидва в 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'
Будь-який набір змінних можна також перетворити в клас. "Змінні" змінні можуть бути додані до екземпляра класу під час виконання, шляхом прямого доступу до вбудованого словника через атрибут __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