Jak osiągnąć zmienne zmienne w Pythonie?
Oto szczegółowy manualny wpis, na przykład: Zmienne zmienne
Słyszałem, że jest to jednak zły pomysł i jest to luka w zabezpieczeniach Pythona. Czy to prawda?
Jak osiągnąć zmienne zmienne w Pythonie?
Oto szczegółowy manualny wpis, na przykład: Zmienne zmienne
Słyszałem, że jest to jednak zły pomysł i jest to luka w zabezpieczeniach Pythona. Czy to prawda?
Odpowiedzi:
Aby to zrobić, możesz użyć słowników . Słowniki to magazyny kluczy i wartości.
>>> dct = {'x': 1, 'y': 2, 'z': 3}
>>> dct
{'y': 2, 'x': 1, 'z': 3}
>>> dct["y"]
2
Możesz użyć nazw kluczy zmiennych, aby uzyskać efekt zmiennych zmiennych bez ryzyka bezpieczeństwa.
>>> x = "spam"
>>> z = {x: "eggs"}
>>> z["spam"]
'eggs'
W przypadkach, w których myślisz o zrobieniu czegoś takiego
var1 = 'foo'
var2 = 'bar'
var3 = 'baz'
...
lista może być bardziej odpowiednia niż dyktando. Lista reprezentuje uporządkowaną sekwencję obiektów z indeksami całkowitymi:
lst = ['foo', 'bar', 'baz']
print(lst[1]) # prints bar, because indices start at 0
lst.append('potatoes') # lst is now ['foo', 'bar', 'baz', 'potatoes']
Dla zamówionych sekwencje, listy są bardziej wygodne niż dicts z kluczami całkowitych, ponieważ wykazy iteracji w celu wspierania indeksu, krojenie , append
i innych operacji, która wymaga zarządzania kluczami niewygodne z dict.
Użyj wbudowanej getattr
funkcji, aby uzyskać atrybut obiektu według nazwy. Zmodyfikuj nazwę zgodnie z potrzebami.
obj.spam = 'eggs'
name = 'spam'
getattr(obj, name) # returns 'eggs'
To nie jest dobry pomysł. Jeśli uzyskujesz dostęp do zmiennej globalnej, możesz jej użyć globals()
.
>>> a = 10
>>> globals()['a']
10
Jeśli chcesz uzyskać dostęp do zmiennej w zasięgu lokalnym, możesz jej użyć locals()
, ale nie możesz przypisać wartości do zwróconego dykta.
Lepszym rozwiązaniem jest użycie getattr
lub przechowywanie zmiennych w słowniku, a następnie dostęp do nich według nazwy.
x = "foo"
i locals()["x"] = "bar"
użycie print x
daje wynik bar
dla Jython 2.5.2. Zostało to przetestowane za pomocą skryptu automatyzacji na żądanie w programie maximo .
Ilekroć chcesz używać zmiennych zmiennych, prawdopodobnie lepiej jest użyć słownika. Więc zamiast pisać
$foo = "bar"
$$foo = "baz"
ty piszesz
mydict = {}
foo = "bar"
mydict[foo] = "baz"
W ten sposób nie zastąpisz przypadkowo wcześniej istniejących zmiennych (co jest aspektem bezpieczeństwa) i możesz mieć różne „przestrzenie nazw”.
Nowi koderzy czasami piszą taki kod:
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)
...
Koderowi pozostawia się stos nazwanych zmiennych, z wysiłkiem kodowania O ( m * n ), gdzie m to liczba nazwanych zmiennych, a n to liczba razy, do której grupa zmiennych musi być dostępna (w tym tworzenie ). Bardziej sprytny początkujący zauważa, że jedyną różnicą w każdej z tych linii jest liczba, która zmienia się w zależności od reguły, i decyduje się na użycie pętli. Utknęli jednak na tym, jak dynamicznie tworzyć te nazwy zmiennych i mogą spróbować czegoś takiego:
for i in range(10):
my_calculator.('button_%d' % i) = tkinter.Button(root, text=i)
Wkrótce przekonują się, że to nie działa.
Jeśli program wymaga dowolnych „nazw zmiennych”, najlepszym wyborem jest słownik, jak wyjaśniono w innych odpowiedziach. Jeśli jednak po prostu próbujesz utworzyć wiele zmiennych i nie masz nic przeciwko odwoływaniu się do nich za pomocą sekwencji liczb całkowitych, prawdopodobnie szukasz list
. Jest to szczególnie prawdziwe, jeśli dane są jednorodne, takie jak codzienne odczyty temperatury, cotygodniowe wyniki quizów lub siatka widżetów graficznych.
Można to złożyć w następujący sposób:
my_calculator.buttons = []
for i in range(10):
my_calculator.buttons.append(tkinter.Button(root, text=i))
To list
może być również tworzone w jednej linii ze zrozumieniem:
my_calculator.buttons = [tkinter.Button(root, text=i) for i in range(10)]
Wynik w obu przypadkach jest zapełniany list
, z pierwszym elementem dostępnym za pomocą my_calculator.buttons[0]
, następnym za pomocą my_calculator.buttons[1]
itd. „Zmienna” nazwa zmiennej staje się nazwą, list
a do uzyskania dostępu używany jest zmienny identyfikator.
Na koniec nie zapomnij o innych strukturach danych, takich jak set
- jest to podobne do słownika, z tym wyjątkiem, że do każdej „nazwy” nie jest przypisana żadna wartość. Jeśli potrzebujesz po prostu „torby” przedmiotów, może to być świetny wybór. Zamiast czegoś takiego:
keyword_1 = 'apple'
keyword_2 = 'banana'
if query == keyword_1 or query == keyword_2:
print('Match.')
Będziesz miał to:
keywords = {'apple', 'banana'}
if query in keywords:
print('Match.')
Użyj a list
do sekwencji podobnych obiektów, a set
do arbitralnie zamówionej torby przedmiotów lub dict
do torby nazw z powiązanymi wartościami.
Zamiast słownika można również użyć namedtuple
z modułu kolekcji, co ułatwia dostęp.
Na przykład:
# 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)
Jeśli nie chcesz używać żadnego obiektu, możesz nadal używać go setattr()
w bieżącym module:
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__
zmienną. Zastanawiam się, czy istnieje ogólny mechanizm dynamicznego tworzenia dowolnej zmiennej globalnej.
globals()
mogę to zrobić
SimpleNamespace
Klasy mogą być wykorzystywane do tworzenia nowych atrybutów z setattr
lub podklasy SimpleNamespace
i stworzyć własną funkcję dodawania nowych nazw atrybutów (zmienne).
from types import SimpleNamespace
variables = {"b":"B","c":"C"}
a = SimpleNamespace(**variables)
setattr(a,"g","G")
a.g = "G+"
something = a.a
Odpowiadam na pytanie: jak uzyskać wartość zmiennej, biorąc pod uwagę jej nazwę w ciągu? który jest zamknięty jako duplikat z linkiem do tego pytania.
Jeśli zmienne w pytaniu są częścią obiektu (część klasy na przykład), a następnie kilka przydatnych funkcji, aby osiągnąć dokładnie, że są hasattr
, getattr
i setattr
.
Na przykład możesz mieć:
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)
Następnie możesz zrobić:
v = Variables()
v.get_var("foo")
„initial_variable”
v.create_new_var(v.foo,"is actually not initial")
v.initial_variable
„tak naprawdę nie jest początkowy”
Posługiwać się globals()
W rzeczywistości możesz dynamicznie przypisywać zmienne do zakresu globalnego, na przykład, jeśli chcesz 10 zmiennych, do których można uzyskać dostęp w zakresie globalnym i_1
, i_2
... i_10
:
for i in range(10):
globals()['i_{}'.format(i)] = 'a'
Spowoduje to przypisanie „a” do wszystkich tych 10 zmiennych, oczywiście można również dynamicznie zmieniać wartość. Wszystkie te zmienne są teraz dostępne, podobnie jak inne globalnie zadeklarowane zmienne:
>>> i_5
'a'
Musisz użyć globals()
wbudowanej metody, aby osiągnąć to zachowanie:
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
Konsensus polega na użyciu do tego słownika - zobacz pozostałe odpowiedzi. Jest to dobry pomysł w większości przypadków, jednak wynika z tego wiele aspektów:
To powiedziawszy, zaimplementowałem klasę menedżera zmiennych zmiennych, która zawiera niektóre z powyższych pomysłów. Działa dla Pythona 2 i 3.
Użyłbyś takiej klasy :
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'])
Jeśli chcesz zezwolić na zastępowanie zmiennych tylko tego samego typu:
myVars = VariableVariablesManager(enforceSameTypeOnOverride = True)
myVars['test'] = 25
myVars['test'] = "Cat" # <- raises Exception (different type on overwriting)
Próbowałem obu w Pythonie 3.7.3, możesz użyć globals () lub 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'
Dowolny zestaw zmiennych można również zawinąć w klasę. Zmienne „Zmienne” można dodawać do instancji klasy podczas działania, bezpośrednio uzyskując dostęp do wbudowanego słownika poprzez atrybut __dict__.
Poniższy kod definiuje klasę Zmienne, która dodaje zmienne (w tym przypadku atrybuty) do swojego wystąpienia podczas budowy. Nazwy zmiennych są pobierane z określonej listy (która na przykład mogła zostać wygenerowana przez kod programu):
# 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