Jak utworzyć zmienną liczbę zmiennych?

363

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?

Taryn
źródło
39
horrory wywołują aspekty związane z utrzymaniem i debugowaniem. Wyobraź sobie, że próbujesz dowiedzieć się, gdzie zmieniła się zmienna „foo”, gdy w kodzie nie ma miejsca, w którym zmienisz „foo”. Wyobraź sobie ponadto, że musisz utrzymywać kod innej osoby ... OK, możesz już iść do szczęśliwego miejsca.
glenn jackman
4
Kolejną pułapką, o której dotychczas nie wspomniano, jest to, że taka dynamicznie tworzona zmienna ma taką samą nazwę jak zmienna używana w twojej logice. Zasadniczo otwierasz swoje oprogramowanie jako zakładnika na dane, które otrzymuje.
holdenweb,
Wszystkie odpowiedzi tutaj zakładają, że masz dostęp do zmiennych podstawowych, do których chcesz uzyskać dostęp dynamicznie według nazwy, co nie zawsze ma miejsce. Myślę, że najbardziej ogólnym podejściem do odtworzenia przykładowego zachowania w PHP jest użycie eval () w następujący sposób: var_name = 'foo'; bar = 5; output = eval (var_name)
Luis Vazquez
1
Możesz modyfikować swoje globalne i lokalne zmienne, uzyskując do nich dostęp do podstawowych słowników; to okropny pomysł z punktu widzenia konserwacji ... ale można to zrobić za pomocą globals (). update () i locals (). update () (lub poprzez zapisanie odnośnika dict z jednego z nich i użycie go jak z każdego innego słownika ). NIE ZALECANE ... ale powinieneś wiedzieć, że jest to możliwe.
Jim Dennis
(Dodam ten komentarz, ponieważ podobne pytanie, a konkretnie prośba o tego rodzaju manipulację, zostało zamknięte za pomocą wskaźnika do tego pytania „wystarczająco blisko”. Chcę, aby osoby podążające za pytaniem zamkniętym otrzymały odpowiedź).
Jim Dennis

Odpowiedzi:

296

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.

Ggorlen
źródło
88

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'
SilentGhost
źródło
70

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.

Nadia Alramli
źródło
locals (). update ({'new_local_var': 'some local value'}) działa dobrze dla mnie w Pythonie 3.7.6; więc nie jestem pewien, co masz na myśli, mówiąc, że nie możesz przez to przypisywać wartości.
Jim Dennis
Podanie 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 .
Kaznodzieja
37

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

sepp2k
źródło
34

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.

TigerhawkT3
źródło
12

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)
ojas mohril
źródło
10

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
Guillaume Lebreton
źródło
Ten brzmi dla mnie lepiej niż używanie „exec”.
fralau
Nie działa to jednak ze __dict__zmienną. Zastanawiam się, czy istnieje ogólny mechanizm dynamicznego tworzenia dowolnej zmiennej globalnej.
Alexey
globals()mogę to zrobić
Guillaume Lebreton
9

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
Bill Oldroyd
źródło
6

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”

patapouf_ai
źródło
5

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'
Rocky Li
źródło
4

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
Andrij Ivaneyko
źródło
3

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:

  • sam będziesz odpowiedzialny za ten słownik, w tym wyrzucanie elementów bezużytecznych (zmiennych in-dict) itp.
  • dla zmiennych zmiennych nie ma ani lokalizacji ani globalności, zależy to od globalności słownika
  • jeśli chcesz zmienić nazwę nazwy zmiennej, musisz to zrobić ręcznie
  • jednak jesteś znacznie bardziej elastyczny, np
    • możesz zdecydować o zastąpieniu istniejących zmiennych lub ...
    • ... wybierz implementację stałych zmiennych
    • zgłosić wyjątek dotyczący nadpisywania dla różnych typów
    • itp.

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)
DomTomCat
źródło
Na pierwszy rzut oka długi kamelizowany import przypomniał mi, że to Java.
markroxor
3

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'


Odniesienie:
https://www.daniweb.com/programming/software-development/threads/111526/setting-a-string-as-a-variable-name#post548936

Hzzkygcs
źródło
0

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
ru13r
źródło