Comment réaliser des variables variables en Python?
Voici une entrée manuelle détaillée, par exemple: Variables variables
J'ai entendu dire que c'est une mauvaise idée en général cependant, et c'est un trou de sécurité en Python. Est-ce vrai?
Comment réaliser des variables variables en Python?
Voici une entrée manuelle détaillée, par exemple: Variables variables
J'ai entendu dire que c'est une mauvaise idée en général cependant, et c'est un trou de sécurité en Python. Est-ce vrai?
Réponses:
Vous pouvez utiliser des 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é de variable pour obtenir l'effet des variables de variable sans risque de sécurité.
>>> x = "spam"
>>> z = {x: "eggs"}
>>> z["spam"]
'eggs'
Pour les cas où vous songez à 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 indices entiers:
lst = ['foo', 'bar', 'baz']
print(lst[1]) # prints bar, because indices start at 0
lst.append('potatoes') # lst is now ['foo', 'bar', 'baz', 'potatoes']
Pour les séquences ordonnées, les listes sont plus pratiques que dicts avec les touches entières, car les listes itération prennent en charge pour l'index, le découpage , append
et d' autres opérations qui requièrent une gestion clé maladroite avec un dict.
Utilisez la getattr
fonction intégrée pour obtenir un attribut sur un objet par son nom. Modifiez le nom selon vos besoins.
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 l'utiliser globals()
.
>>> a = 10
>>> globals()['a']
10
Si vous souhaitez accéder à une variable dans la portée locale, vous pouvez l'utiliser locals()
, mais vous ne pouvez pas affecter de valeurs au dict renvoyé.
Une meilleure solution consiste à utiliser getattr
ou à stocker vos variables dans un dictionnaire, puis à y accéder par nom.
Chaque fois que 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 ceci:
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 accédé (y compris la création ). Le débutant le plus astucieux observe que la seule différence dans chacune de ces lignes est un nombre qui change en fonction d'une règle, et décide d'utiliser une boucle. Cependant, ils se retrouvent bloqués sur la façon de créer dynamiquement ces noms de variables et peuvent essayer quelque chose comme ceci:
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 séquence d'entiers, vous recherchez probablement un list
. Cela est particulièrement vrai si vos données sont homogènes, telles que les relevés de température quotidiens, les scores de quiz hebdomadaires ou une grille de widgets graphiques.
Cela peut être assemblé comme suit:
my_calculator.buttons = []
for i in range(10):
my_calculator.buttons.append(tkinter.Button(root, text=i))
Cela list
peut également être créé en une seule 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 remplissage list
, avec le premier élément accédé avec my_calculator.buttons[0]
, le suivant avec my_calculator.buttons[1]
, et ainsi de suite. Le nom de la variable "base" devient le nom du list
et l'identifiant variable est utilisé pour y accéder.
Enfin, n'oubliez pas d'autres structures de données, telles que le set
- c'est similaire à un dictionnaire, sauf que chaque "nom" n'a pas de valeur attachée. 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 a list
pour une séquence d'objets similaires, a set
pour un sac d'objets commandés arbitrairement ou a 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)
Si vous ne voulez utiliser aucun objet, vous pouvez toujours utiliser à l' setattr()
intérieur de 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
__dict__
Cependant, cela ne fonctionne pas avec la variable. Je me demande s'il existe un mécanisme général pour créer une dynamique variable globale.
globals()
peut le faire
La SimpleNamespace
classe peut être utilisée pour créer de nouveaux attributs avec setattr
, ou sous SimpleNamespace
- classe et créer votre propre fonction pour ajouter de nouveaux noms d'attribut (variables).
from types import SimpleNamespace
variables = {"b":"B","c":"C"}
a = SimpleNamespace(**variables)
setattr(a,"g","G")
a.g = "G+"
something = a.a
Je réponds à la question: comment obtenir la valeur d'une variable étant donné son nom dans une chaîne? qui est fermé en double avec un lien vers cette question.
Si les variables en question font partie d'un objet ( une partie d'une classe par exemple) , alors certaines fonctions utiles pour atteindre ce sont exactement 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_initiale"
v.create_new_var(v.foo,"is actually not initial")
v.initial_variable
"n'est en fait pas initial"
Utilisation globals()
Vous pouvez réellement affecter des variables à la portée globale de façon dynamique, par exemple, si vous voulez 10 variables 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» à toutes ces 10 variables, bien sûr, vous pouvez également modifier la valeur dynamiquement. Toutes ces variables sont accessibles maintenant comme les autres variables déclarées globalement:
>>> i_5
'a'
Vous devez utiliser la 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
Le consensus est d'utiliser un dictionnaire pour cela - voir les autres réponses. C'est une bonne idée pour la plupart des cas, cependant, il y a de nombreux aspects qui en découlent:
Cela dit, j'ai implémenté un gestionnaire de variables variables -class 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 l'écrasement de variables de même type uniquement:
myVars = VariableVariablesManager(enforceSameTypeOnOverride = True)
myVars['test'] = 25
myVars['test'] = "Cat" # <- raises Exception (different type on overwriting)
J'ai essayé les deux en python 3.7.3, vous pouvez utiliser soit globals () ou 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'
Tout ensemble de variables peut également être enveloppé 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 pendant la construction. Les noms de variables sont tirés 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