Come faccio a creare un numero variabile di variabili?


364

Come realizzo variabili variabili in Python?

Ecco una voce manuale elaborativa, ad esempio: Variabili variabili

Ho sentito che questa è una cattiva idea in generale, ed è una falla nella sicurezza di Python. È vero?


39
sono gli aspetti di manutenzione e debug che causano l'orrore. Immagina di provare a scoprire dove è cambiata la variabile 'pippo' quando non c'è posto nel tuo codice dove cambi davvero 'pippo'. Immagina inoltre che è il codice di qualcun altro che devi mantenere ... OK, ora puoi andare nel tuo posto felice.
Glenn Jackman,

4
Un ulteriore insulto che non è stato menzionato finora è se una variabile così creata dinamicamente ha lo stesso nome di una variabile utilizzata nella tua logica. In sostanza, apri il tuo software come ostaggio all'input che gli viene dato.
Holdenweb,

Tutte le risposte qui presuppongono che tu abbia accesso alle variabili di base a cui vuoi accedere dinamicamente per nome, il che non è sempre il caso. Penso che l'approccio più generale per riprodurre il comportamento di esempio in PHP sia usare eval () in questo modo: var_name = 'foo'; bar = 5; output = eval (var_name)
Luis Vazquez,

1
Puoi modificare le variabili globali e locali accedendo ai dizionari sottostanti; è un'idea orribile dal punto di vista della manutenzione ... ma può essere fatto tramite globals (). update () e locals (). update () (o salvando il riferimento dict da uno di questi e utilizzandolo come qualsiasi altro dizionario ). NON CONSIGLIATO ... ma dovresti sapere che è possibile.
Jim Dennis il

(Sto aggiungendo questo commento perché una domanda simile, in particolare la richiesta di questo tipo di manipolazione, è stata chiusa con un puntatore a questa domanda "abbastanza vicina". Voglio che quelli che seguono la domanda chiusa ottengano la loro risposta).
Jim Dennis il

Risposte:


297

È possibile utilizzare i dizionari per ottenere questo risultato. I dizionari sono archivi di chiavi e valori.

>>> dct = {'x': 1, 'y': 2, 'z': 3}
>>> dct
{'y': 2, 'x': 1, 'z': 3}
>>> dct["y"]
2

È possibile utilizzare i nomi delle chiavi delle variabili per ottenere l'effetto delle variabili senza il rischio per la sicurezza.

>>> x = "spam"
>>> z = {x: "eggs"}
>>> z["spam"]
'eggs'

Per i casi in cui stai pensando di fare qualcosa del genere

var1 = 'foo'
var2 = 'bar'
var3 = 'baz'
...

un elenco può essere più appropriato di un dict. Un elenco rappresenta una sequenza ordinata di oggetti, con indici interi:

lst = ['foo', 'bar', 'baz']
print(lst[1])           # prints bar, because indices start at 0
lst.append('potatoes')  # lst is now ['foo', 'bar', 'baz', 'potatoes']

Per le sequenze ordinate, elenchi sono più convenienti di dicts con chiavi intere, perché le liste iterazione supportano in ordine di indice, affettare , appende altre operazioni che richiederebbero gestione delle chiavi imbarazzante con un dict.


88

Utilizzare la getattrfunzione integrata per ottenere un attributo su un oggetto per nome. Modifica il nome secondo necessità.

obj.spam = 'eggs'
name = 'spam'
getattr(obj, name)  # returns 'eggs'

70

Non è una buona idea. Se si accede a una variabile globale è possibile utilizzare globals().

>>> a = 10
>>> globals()['a']
10

Se si desidera accedere a una variabile nell'ambito locale è possibile utilizzare locals(), ma non è possibile assegnare valori al dict restituito.

Una soluzione migliore è utilizzare getattro archiviare le variabili in un dizionario e quindi accedervi per nome.


locals (). update ({'new_local_var': 'some local value'}) funziona bene per me in Python 3.7.6; quindi non sono sicuro di cosa intendi quando dici che non puoi assegnare valori attraverso di esso.
Jim Dennis il

Dato x = "foo"e locals()["x"] = "bar"utilizzato print xfornisce l'output barper Jython 2.5.2. Questo è stato testato con uno script di automazione su richiesta in maximo .
Predicatore

37

Ogni volta che vuoi usare variabili variabili, probabilmente è meglio usare un dizionario. Quindi invece di scrivere

$foo = "bar"
$$foo = "baz"

Scrivi

mydict = {}
foo = "bar"
mydict[foo] = "baz"

In questo modo non si sovrascriveranno accidentalmente variabili precedentemente esistenti (che è l'aspetto della sicurezza) e si possono avere diversi "spazi dei nomi".


34

I nuovi programmatori a volte scrivono codice in questo modo:

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

Il codificatore viene quindi lasciato con una pila di variabili denominate, con uno sforzo di codifica di O ( m * n ), dove m è il numero di variabili nominate e n è il numero di volte in cui è necessario accedere al gruppo di variabili (inclusa la creazione ). Il principiante più astuto osserva che l'unica differenza in ciascuna di quelle righe è un numero che cambia in base a una regola e decide di utilizzare un ciclo. Tuttavia, rimangono bloccati su come creare dinamicamente quei nomi di variabili e possono provare qualcosa del genere:

for i in range(10):
    my_calculator.('button_%d' % i) = tkinter.Button(root, text=i)

Presto scoprono che questo non funziona.

Se il programma richiede "nomi" variabili arbitrari, un dizionario è la scelta migliore, come spiegato in altre risposte. Tuttavia, se stai semplicemente cercando di creare molte variabili e non ti dispiace fare riferimento a esse con una sequenza di numeri interi, probabilmente stai cercando unlist . Ciò è particolarmente vero se i tuoi dati sono omogenei, come letture della temperatura giornaliera, punteggi settimanali dei quiz o una griglia di widget grafici.

Questo può essere assemblato come segue:

my_calculator.buttons = []
for i in range(10):
    my_calculator.buttons.append(tkinter.Button(root, text=i))

Questo listpuò anche essere creato in una riga con una comprensione:

my_calculator.buttons = [tkinter.Button(root, text=i) for i in range(10)]

Il risultato in entrambi i casi è popolato list, con l'accesso al primo elemento my_calculator.buttons[0], il successivo con my_calculator.buttons[1]e così via. Il nome della variabile "base" diventa il nome del filelist e l'identificatore variabile viene utilizzato per accedervi.

Infine, non dimenticare altre strutture di dati, come il set- questo è simile a un dizionario, tranne per il fatto che ogni "nome" non ha un valore associato. Se hai semplicemente bisogno di una "borsa" di oggetti, questa può essere un'ottima scelta. Invece di qualcosa del genere:

keyword_1 = 'apple'
keyword_2 = 'banana'

if query == keyword_1 or query == keyword_2:
    print('Match.')

Avrai questo:

keywords = {'apple', 'banana'}
if query in keywords:
    print('Match.')

Utilizzare a listper una sequenza di oggetti simili, a setper un sacchetto di oggetti ordinato in modo arbitrario o a dictper un sacchetto di nomi con valori associati.


12

Invece di un dizionario puoi anche usare namedtuple dal modulo collezioni, che semplifica l'accesso.

Per esempio:

# 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

Se non si desidera utilizzare alcun oggetto, è comunque possibile utilizzare setattr()all'interno del modulo corrente:

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

Questo suona meglio per me rispetto all'uso di "exec".
fralau,

__dict__Tuttavia, questo non funziona con le variabili. Mi chiedo se esiste un meccanismo generale per creare dinamicamente qualsiasi variabile globale.
Alexey,

globals()posso farlo
Guillaume Lebreton

9

La SimpleNamespaceclasse può essere utilizzata per creare nuovi attributi con setattro sottoclasse SimpleNamespacee creare la propria funzione per aggiungere nuovi nomi di attributi (variabili).

from types import SimpleNamespace

variables = {"b":"B","c":"C"}
a = SimpleNamespace(**variables)
setattr(a,"g","G")
a.g = "G+"
something = a.a

6

Sto rispondendo alla domanda: come ottenere il valore di una variabile dato il suo nome in una stringa? che è chiuso come duplicato con un link a questa domanda.

Se le variabili in questione sono parte di un oggetto (parte di una classe ad esempio), allora alcune funzioni utili per ottenere esattamente che sono hasattr, getattresetattr .

Quindi ad esempio puoi avere:

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)

Quindi puoi fare:

v = Variables()
v.get_var("foo")

"Initial_variable"

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

"in realtà non è iniziale"


6

Uso globals()

Puoi effettivamente assegnare variabili all'ambito globale in modo dinamico, ad esempio, se vuoi 10 variabili a cui è possibile accedere su un ambito globale i_1, i_2... i_10:

for i in range(10):
    globals()['i_{}'.format(i)] = 'a'

Questo assegnerà 'a' a tutte queste 10 variabili, ovviamente puoi anche cambiare il valore in modo dinamico. È ora possibile accedere a tutte queste variabili come altre variabili dichiarate a livello globale:

>>> i_5
'a'

4

Devi usare il globals()metodo integrato per ottenere quel comportamento:

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

Il consenso è usare un dizionario per questo - vedi le altre risposte. Questa è una buona idea per la maggior parte dei casi, tuttavia ci sono molti aspetti che ne derivano:

  • sarai tu stesso responsabile di questo dizionario, inclusa la garbage collection (delle variabili in-dict) ecc.
  • non c'è né località né globalità per le variabili variabili, dipende dalla globalità del dizionario
  • se vuoi rinominare un nome di variabile, dovrai farlo manualmente
  • tuttavia, sei molto più flessibile, ad es
    • puoi decidere di sovrascrivere le variabili esistenti o ...
    • ... scegliere di implementare variabili const
    • per sollevare un'eccezione sulla sovrascrittura per diversi tipi
    • eccetera.

Detto questo, ho implementato una classe di variabili variabili che fornisce alcune delle idee sopra. Funziona con Python 2 e 3.

Utilizzeresti la classe in questo modo:

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'])

Se si desidera consentire la sovrascrittura delle variabili solo con lo stesso tipo:

myVars = VariableVariablesManager(enforceSameTypeOnOverride = True)
myVars['test'] = 25
myVars['test'] = "Cat" # <- raises Exception (different type on overwriting)

A prima vista le lunghe importazioni camelizzate mi hanno fatto pensare che si trattasse di Java.
Markroxor,

3

Ho provato entrambi in Python 3.7.3, puoi usare globals () o 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'


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


0

Qualsiasi set di variabili può anche essere racchiuso in una classe. Le variabili "variabili" possono essere aggiunte all'istanza della classe durante il runtime accedendo direttamente al dizionario incorporato tramite l'attributo __dict__.

Il codice seguente definisce la classe Variables, che aggiunge variabili (in questo caso attributi) alla sua istanza durante la costruzione. I nomi delle variabili sono presi da un elenco specificato (che, ad esempio, avrebbe potuto essere generato dal codice del programma):

# 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.