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 , appendi innych operacji, która wymaga zarządzania kluczami niewygodne z dict.
Użyj wbudowanej getattrfunkcji, 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 getattrlub przechowywanie zmiennych w słowniku, a następnie dostęp do nich według nazwy.
x = "foo"i locals()["x"] = "bar"użycie print xdaje wynik bardla 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 listmoż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ą, lista 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 listdo sekwencji podobnych obiektów, a setdo arbitralnie zamówionej torby przedmiotów lub dictdo torby nazw z powiązanymi wartościami.
Zamiast słownika można również użyć namedtuplez 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ć
SimpleNamespaceKlasy mogą być wykorzystywane do tworzenia nowych atrybutów z setattrlub podklasy SimpleNamespacei 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, getattri 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