Comment puis-je accomplir des variables variables en Python?
Voici une entrée manuelle élaborative, par exemple: variables variables
J'ai entendu dire que c'était une mauvaise idée en général, et que c'était un trou de sécurité en Python. Est-ce vrai?
Vous pouvez utiliser dictionnaires pour accomplir cela. Les dictionnaires sont des magasins de clés et de valeurs.
>>> dct = {'x': 1, 'y': 2, 'z': 3}
>>> dct
{'y': 2, 'x': 1, 'z': 3}
>>> dct["y"]
2
Vous pouvez utiliser des noms de clé variables pour obtenir l'effet de variables variables sans risque de sécurité.
>>> x = "spam"
>>> z = {x: "eggs"}
>>> z["spam"]
'eggs'
Pour les cas où vous envisagez de faire quelque chose comme
var1 = 'foo'
var2 = 'bar'
var3 = 'baz'
...
une liste peut être plus appropriée qu'un dict. Une liste représente une séquence ordonnée d'objets, avec des index entiers:
l = ['foo', 'bar', 'baz']
print(l[1]) # prints bar, because indices start at 0
l.append('potatoes') # l is now ['foo', 'bar', 'baz', 'potatoes']
Pour les séquences ordonnées, les listes sont plus pratiques que les dictées avec des clés entières, car elles prennent en charge l’itération dans l’ordre des index, découpage , append
et d’autres opérations qui nécessiteraient une gestion fastidieuse des clés avec un dict.
Utilisez la fonction intégrée getattr
pour obtenir un attribut sur un objet par son nom. Modifiez le nom si nécessaire.
obj.spam = 'eggs'
name = 'spam'
getattr(obj, name) # returns 'eggs'
Ce n'est pas une bonne idée. Si vous accédez à une variable globale, vous pouvez utiliser globals()
.
>>> a = 10
>>> globals()['a']
10
Si vous souhaitez accéder à une variable de la portée locale, vous pouvez utiliser locals()
, mais vous ne pouvez pas affecter de valeurs au dict retourné.
Une meilleure solution consiste à utiliser getattr
ou à stocker vos variables dans un dictionnaire, puis à y accéder par leur nom.
Lorsque vous souhaitez utiliser des variables variables, il est probablement préférable d'utiliser un dictionnaire. Donc au lieu d'écrire
$foo = "bar"
$$foo = "baz"
vous écrivez
mydict = {}
foo = "bar"
mydict[foo] = "baz"
De cette façon, vous n'écraserez pas accidentellement des variables existantes (ce qui est l'aspect sécurité) et vous pouvez avoir différents "espaces de noms".
Les nouveaux codeurs écrivent parfois du code comme celui-ci:
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)
...
Le codeur se retrouve alors avec une pile de variables nommées, avec un effort de codage de O (m * n), où m est le nombre de variables nommées et n est le nombre de fois que ce groupe de variables doit être consulté (création comprise). Le débutant plus astucieux observe que la seule différence entre chacune de ces lignes est un nombre qui change en fonction d’une règle et décide d’utiliser une boucle. Cependant, ils ne savent pas comment créer dynamiquement ces noms de variables et peuvent essayer quelque chose comme ça:
for i in range(10):
my_calculator.('button_%d' % i) = tkinter.Button(root, text=i)
Ils constatent rapidement que cela ne fonctionne pas.
Si le programme nécessite des "noms" de variables arbitraires, un dictionnaire est le meilleur choix, comme expliqué dans d'autres réponses. Cependant, si vous essayez simplement de créer de nombreuses variables et que cela ne vous dérange pas de vous y référer avec une suite d’entiers, vous recherchez probablement un list
. Cela est particulièrement vrai si vos données sont homogènes, telles que les lectures quotidiennes de la température, les scores hebdomadaires du quiz ou une grille de widgets graphiques.
Ceci peut être assemblé comme suit:
my_calculator.buttons = []
for i in range(10):
my_calculator.buttons.append(tkinter.Button(root, text=i))
Cette list
peut également être créée en une ligne avec une compréhension:
my_calculator.buttons = [tkinter.Button(root, text=i) for i in range(10)]
Dans les deux cas, le résultat est un list
rempli, avec le premier élément auquel accéder avec my_calculator.buttons[0]
, le suivant avec my_calculator.buttons[1]
, etc. Le nom de la variable "base" devient le nom de la list
et l'identifiant variable est utilisé pour y accéder.
Enfin, n'oubliez pas les autres structures de données, telles que set
- cela ressemble à un dictionnaire, sauf que chaque "nom" n'a pas de valeur. Si vous avez simplement besoin d'un "sac" d'objets, cela peut être un excellent choix. Au lieu de quelque chose comme ça:
keyword_1 = 'Apple'
keyword_2 = 'banana'
if query == keyword_1 or query == keyword_2:
print('Match.')
Vous aurez ceci:
keywords = {'Apple', 'banana'}
if query in keywords:
print('Match.')
Utilisez un list
pour une séquence d'objets similaires, un set
pour un sac d'objets ordonné de manière arbitraire ou un dict
pour un sac de noms avec des valeurs associées.
Au lieu d'un dictionnaire, vous pouvez également utiliser namedtuple
à partir du module Collections, ce qui facilite l'accès.
Par exemple:
# 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)
La classe SimpleNamespace
peut être utilisée pour créer de nouveaux attributs avec setattr
ou la sous-classe SimpleNamespace
et créer votre propre fonction pour ajouter de nouveaux noms d'attributs (variables).
from types import SimpleNamespace
variables = {"b":"B","c":"C"}
a = SimpleNamespace(**variables)
setattr(a,"g","G")
a.g = "G+"
something = a.a
Si vous ne souhaitez utiliser aucun objet, vous pouvez toujours utiliser setattr()
dans votre module actuel:
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
Vous devez utiliser globals()
méthode intégrée pour obtenir ce comportement:
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
Je réponds à la question: Comment obtenir la valeur d'une variable étant donné son nom dans une chaîne? qui est fermé comme un doublon avec un lien vers cette question.
Si les variables en question font partie d'un objet (par exemple, une partie d'une classe), certaines des fonctions utiles pour obtenir exactement ce résultat sont hasattr
, getattr
et setattr
.
Ainsi, par exemple, vous pouvez avoir:
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)
Ensuite, vous pouvez faire:
v = Variables()
v.get_var("foo")
"variable_initial"
v.create_new_var(v.foo,"is actually not initial")
v.initial_variable
"n'est en réalité pas initiale"
Utiliser globals()
Vous pouvez en fait affecter dynamiquement des variables à la portée globale, par exemple, si vous souhaitez que 10 variables soient accessibles sur une portée globale i_1
, i_2
... i_10
:
for i in range(10):
globals()['i_{}'.format(i)] = 'a'
Cela affectera "a" à l'ensemble de ces 10 variables. Vous pourrez bien entendu modifier la valeur de manière dynamique. Toutes ces variables sont accessibles maintenant comme les autres variables déclarées globalement:
>>> i_5
'a'
Il existe une méthode connue pour émuler un conteneur de variables, qui supporte les deux méthodes d'accès: par le nom d'une variable et par une clé de chaîne.
class Vars:
def __init__(self, **kw):
self.__dict__.update(kw)
def __getitem__(self, key):
return self.__dict__[key]
def __setitem__(self, key, val):
self.__dict__[key] = val
def __contains__(self, name):
return name in self.__dict__
def __nonzero__(self):
return bool(self.__dict__)
def __iter__(self):
return iter(self.__dict__)
def __len__(self):
return len(self.__dict__)
def __copy__(self):
return self.__class__(**self.__dict__)
def __repr__(self):
return 'Vars(' + ', '.join('%s=%r' % (k,v) for k,v in self.__dict__.items()) + ')'
>>> vars = Vars()
>>> vars.a = 1
>>> vars['b'] = 2
>>> print(vars)
Vars(a=1, b=2)
>>> print(vars['a'], vars.b)
1 2
>>> print(Tuple(vars))
('a', 'b')
Le consensus est d'utiliser un dictionnaire pour cela - voir les autres réponses. C’est une bonne idée dans la plupart des cas, cependant, il en résulte de nombreux aspects:
Cela dit, j'ai implémenté une classe gestionnaire de variables variables - qui fournit certaines des idées ci-dessus. Cela fonctionne pour python 2 et 3.
Vous utiliseriez la classe comme ceci:
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'])
Si vous souhaitez autoriser le remplacement de variables du même type uniquement:
myVars = VariableVariablesManager(enforceSameTypeOnOverride = True)
myVars['test'] = 25
myVars['test'] = "Cat" # <- raises Exception (different type on overwriting)
Veuillez vous référer à l'exemple suivant pour créer des variables d'exécution. Vous pouvez utiliser globals()
.
for i in range(3):
globals() ['variable_'+str(i)] = i
Dans l'exemple ci-dessus, je souhaite créer trois variables: variable_0
, variable_1
et variable_2
à l'exécution avec les valeurs 0,1 et 2 respectivement.
variable_0
[Output]:0
variable_1
[Output]:1
variable_2
[Output]:2
Pour accéder à la valeur des variables créées à l'exécution, vous pouvez utiliser la méthode eval()
comme suit:
for i in range(3):
globals() ['variable_'+str(i)] = i
print('Variable Value:',eval('variable_'+str(i)))
[Output]:
Variable Value: 0
Variable Value: 1
Variable Value: 2
Tout ensemble de variables peut également être encapsulé dans une classe. Des variables "variables" peuvent être ajoutées à l'instance de classe pendant l'exécution en accédant directement au dictionnaire intégré via l'attribut __dict__.
Le code suivant définit la classe Variables, qui ajoute des variables (dans ce cas, des attributs) à son instance lors de la construction. Les noms de variables proviennent d'une liste spécifiée (qui, par exemple, aurait pu être générée par le code du programme):
# 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
vous pouvez utiliser la fonction intégrée vars()
>>> foo = 'bar'
>>> vars()[foo] = 'something'
>>> bar
'something'