La constante dynamique est quelque chose comme l'eau sèche? :)
fl00r
39
Cela ne dit pas que la constante est dynamique. Il dit que la mission est dynamique.
sepp2k
Réponses:
141
Votre problème est que chaque fois que vous exécutez la méthode, vous attribuez une nouvelle valeur à la constante. Ceci n'est pas autorisé, car cela rend la constante non constante; même si le contenu de la chaîne est le même (pour le moment, en tout cas), l' objet chaîne lui-même est différent à chaque fois que la méthode est appelée. Par exemple:
def foo
p "bar".object_id
end
foo #=> 15779172
foo #=> 15779112
Peut-être que si vous expliquiez votre cas d'utilisation - pourquoi vous voulez changer la valeur d'une constante dans une méthode - nous pourrions vous aider avec une meilleure implémentation.
Peut-être préférez-vous avoir une variable d'instance sur la classe?
classMyClassclass<<self
attr_accessor :my_constant
enddef my_method
self.class.my_constant ="blah"endend
p MyClass.my_constant #=> nilMyClass.new.my_method
p MyClass.my_constant #=> "blah"
Si vous voulez vraiment changer la valeur d'une constante dans une méthode et que votre constante est une chaîne ou un tableau, vous pouvez `` tricher '' et utiliser la #replaceméthode pour que l'objet prenne une nouvelle valeur sans changer réellement l'objet:
classMyClass
BAR ="blah"def cheat(new_bar)
BAR.replace new_bar
endend
p MyClass::BAR #=> "blah"MyClass.new.cheat "whee"
p MyClass::BAR #=> "whee"
L'OP n'a jamais dit qu'il voulait changer la valeur de la constante, mais voulait simplement attribuer une valeur. Le cas d'utilisation fréquent menant à cette erreur Ruby est lorsque vous construisez la valeur dans une méthode à partir d'autres actifs d'exécution (variables, arguments de ligne de commande, ENV), généralement dans un constructeur, par exemple def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end. C'est l'un de ces cas où Ruby n'a pas de moyen simple.
Arnaud Meuret
2
@ArnaudMeuret Dans ce cas, vous voulez une variable d'instance (par exemple @variable), pas une constante. Sinon, vous réassigneriez DBchaque fois que vous instanciez une nouvelle instance de cette classe.
Ajedi32
2
@ Ajedi32 Cette situation résulte généralement de contraintes externes et non de choix de conception comme mon exemple avec Sequel. Mon point est que l'attribution d'une valeur à une constante est autorisée par Ruby dans certaines étendues et pas dans d'autres. Auparavant, il appartenait au développeur de choisir judicieusement quand effectuer la mission. Ruby a changé à ce sujet. Pas pour le bien de tout le monde.
Arnaud Meuret
2
@ArnaudMeuret J'admets que je n'ai jamais utilisé Sequel auparavant, je ne peux donc pas le dire avec une certitude à 100%, mais en jetant un coup d'œil sur la documentation de Sequel, je ne vois rien qui indique que vous DEVEZ attribuer le résultat de Sequel.connectà une constante nommée DB . En fait, la documentation dit explicitement que ce n'est qu'une recommandation. Cela ne me semble pas une contrainte extérieure.
Ajedi32
@ Ajedi32 1) Je n'ai jamais écrit que (nom de la constante ou même que vous deviez la garder quelque part) c'est juste un exemple 2) La contrainte étant que votre logiciel peut ne pas avoir les informations nécessaires jusqu'à ce que vous soyez généralement dans un contexte dynamique .
Arnaud Meuret
69
Étant donné que les constantes de Ruby ne sont pas censées être modifiées, Ruby vous déconseille de leur attribuer des parties de code qui pourraient être exécutées plus d'une fois, comme les méthodes internes.
Dans des circonstances normales, vous devez définir la constante à l'intérieur de la classe elle-même:
Si, pour une raison quelconque, vous avez vraiment besoin de définir une constante à l'intérieur d'une méthode (peut-être pour un type de métaprogrammation), vous pouvez utiliser const_set:
Encore une fois, ce const_setn'est pas quelque chose auquel vous devriez vraiment avoir recours dans des circonstances normales. Si vous ne savez pas si vous voulez vraiment attribuer des constantes de cette façon, vous pouvez envisager l'une des alternatives suivantes:
Variables de classe
Les variables de classe se comportent comme des constantes à bien des égards. Ce sont des propriétés sur une classe et elles sont accessibles dans les sous-classes de la classe sur laquelle elles sont définies.
La différence est que les variables de classe sont censées être modifiables et peuvent donc être affectées à des méthodes internes sans problème.
classMyClassdefself.my_class_variable
@@my_class_variableenddef my_method
@@my_class_variable="foo"endendclassSubClass<MyClassendMyClass.my_class_variable
#=> NameError: uninitialized class variable @@my_class_variable in MyClassSubClass.my_class_variable
#=> NameError: uninitialized class variable @@my_class_variable in MyClassMyClass.new.my_method
MyClass.my_class_variable #=> "foo"SubClass.my_class_variable #=> "foo"
Attributs de classe
Les attributs de classe sont une sorte de "variable d'instance sur une classe". Ils se comportent un peu comme des variables de classe, sauf que leurs valeurs ne sont pas partagées avec les sous-classes.
Et juste pour être complet, je devrais probablement mentionner: si vous avez besoin d'attribuer une valeur qui ne peut être déterminée qu'après que votre classe a été instanciée, il y a de bonnes chances que vous recherchiez une variable d'instance ancienne.
Dans Ruby, toute variable dont le nom commence par une majuscule est une constante et vous ne pouvez lui attribuer qu'une seule fois. Choisissez l'une de ces alternatives:
Vous ne pouvez pas nommer une variable avec des lettres majuscules ou Ruby assumera sa constante et voudra qu'elle garde sa valeur constante, auquel cas changer sa valeur serait une erreur une "erreur d'affectation de constante dynamique". Avec des minuscules devrait être bien
Ruby n'aime pas que vous affectiez la constante à l'intérieur d'une méthode car cela risque de se réaffecter. Plusieurs réponses SO avant moi donnent l'alternative de l'attribuer en dehors d'une méthode - mais dans la classe, qui est un meilleur endroit pour l'attribuer.
Weicome à SO John. Vous pouvez envisager d'améliorer cette réponse en ajoutant un exemple de code de ce que vous décrivez.
Cleptus
0
Un grand merci à Dorian et Phrogz pour m'avoir rappelé la méthode de tableau (et de hachage) #replace, qui peut "remplacer le contenu d'un tableau ou d'un hachage".
La notion selon laquelle la valeur d'un CONSTANT peut être modifiée, mais avec un avertissement ennuyeux, est l'une des rares erreurs conceptuelles de Ruby - celles-ci devraient soit être totalement immuables, soit vider complètement l'idée de constante. Du point de vue d'un codeur, une constante est déclarative et intentionnelle, un signal à l'autre que «cette valeur est vraiment immuable une fois déclarée / attribuée».
Mais parfois, une «déclaration évidente» exclut en fait d'autres opportunités utiles futures. Par exemple...
Il y a des cas d'utilisation légitimes où la valeur d'une «constante» peut vraiment avoir besoin d'être modifiée: par exemple, recharger ARGV à partir d'une boucle d'invite de type REPL, puis réexécuter ARGV via plus d'OptionParser.parse (suivant)! appels - voila! Donne aux "arguments de ligne de commande" un tout nouvel utilitaire dynamique.
Le problème pratique est soit avec l'hypothèse présumée que "ARGV doit être une constante", soit dans la propre méthode d'initialisation d'optparse, qui code en dur l'affectation d'ARGV à l'instance var @default_argv pour un traitement ultérieur - ce tableau (ARGV) devrait être un paramètre encourageant la ré-analyse et la réutilisation, le cas échéant. Un paramétrage correct, avec une valeur par défaut appropriée (par exemple, ARGV) éviterait de devoir jamais changer l'ARGV «constant». Juste quelques 2 ¢ dignes de réflexion ...
Réponses:
Votre problème est que chaque fois que vous exécutez la méthode, vous attribuez une nouvelle valeur à la constante. Ceci n'est pas autorisé, car cela rend la constante non constante; même si le contenu de la chaîne est le même (pour le moment, en tout cas), l' objet chaîne lui-même est différent à chaque fois que la méthode est appelée. Par exemple:
Peut-être que si vous expliquiez votre cas d'utilisation - pourquoi vous voulez changer la valeur d'une constante dans une méthode - nous pourrions vous aider avec une meilleure implémentation.
Peut-être préférez-vous avoir une variable d'instance sur la classe?
Si vous voulez vraiment changer la valeur d'une constante dans une méthode et que votre constante est une chaîne ou un tableau, vous pouvez `` tricher '' et utiliser la
#replace
méthode pour que l'objet prenne une nouvelle valeur sans changer réellement l'objet:la source
def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end
. C'est l'un de ces cas où Ruby n'a pas de moyen simple.@variable
), pas une constante. Sinon, vous réassigneriezDB
chaque fois que vous instanciez une nouvelle instance de cette classe.Sequel.connect
à une constante nommée DB . En fait, la documentation dit explicitement que ce n'est qu'une recommandation. Cela ne me semble pas une contrainte extérieure.Étant donné que les constantes de Ruby ne sont pas censées être modifiées, Ruby vous déconseille de leur attribuer des parties de code qui pourraient être exécutées plus d'une fois, comme les méthodes internes.
Dans des circonstances normales, vous devez définir la constante à l'intérieur de la classe elle-même:
Si, pour une raison quelconque, vous avez vraiment besoin de définir une constante à l'intérieur d'une méthode (peut-être pour un type de métaprogrammation), vous pouvez utiliser
const_set
:Encore une fois, ce
const_set
n'est pas quelque chose auquel vous devriez vraiment avoir recours dans des circonstances normales. Si vous ne savez pas si vous voulez vraiment attribuer des constantes de cette façon, vous pouvez envisager l'une des alternatives suivantes:Variables de classe
Les variables de classe se comportent comme des constantes à bien des égards. Ce sont des propriétés sur une classe et elles sont accessibles dans les sous-classes de la classe sur laquelle elles sont définies.
La différence est que les variables de classe sont censées être modifiables et peuvent donc être affectées à des méthodes internes sans problème.
Attributs de classe
Les attributs de classe sont une sorte de "variable d'instance sur une classe". Ils se comportent un peu comme des variables de classe, sauf que leurs valeurs ne sont pas partagées avec les sous-classes.
Variables d'instance
Et juste pour être complet, je devrais probablement mentionner: si vous avez besoin d'attribuer une valeur qui ne peut être déterminée qu'après que votre classe a été instanciée, il y a de bonnes chances que vous recherchiez une variable d'instance ancienne.
la source
Dans Ruby, toute variable dont le nom commence par une majuscule est une constante et vous ne pouvez lui attribuer qu'une seule fois. Choisissez l'une de ces alternatives:
la source
Les constantes de ruby ne peuvent pas être définies dans les méthodes. Voir les notes au bas de cette page, par exemple
la source
Vous ne pouvez pas nommer une variable avec des lettres majuscules ou Ruby assumera sa constante et voudra qu'elle garde sa valeur constante, auquel cas changer sa valeur serait une erreur une "erreur d'affectation de constante dynamique". Avec des minuscules devrait être bien
la source
Ruby n'aime pas que vous affectiez la constante à l'intérieur d'une méthode car cela risque de se réaffecter. Plusieurs réponses SO avant moi donnent l'alternative de l'attribuer en dehors d'une méthode - mais dans la classe, qui est un meilleur endroit pour l'attribuer.
la source
Un grand merci à Dorian et Phrogz pour m'avoir rappelé la méthode de tableau (et de hachage) #replace, qui peut "remplacer le contenu d'un tableau ou d'un hachage".
La notion selon laquelle la valeur d'un CONSTANT peut être modifiée, mais avec un avertissement ennuyeux, est l'une des rares erreurs conceptuelles de Ruby - celles-ci devraient soit être totalement immuables, soit vider complètement l'idée de constante. Du point de vue d'un codeur, une constante est déclarative et intentionnelle, un signal à l'autre que «cette valeur est vraiment immuable une fois déclarée / attribuée».
Mais parfois, une «déclaration évidente» exclut en fait d'autres opportunités utiles futures. Par exemple...
Il y a des cas d'utilisation légitimes où la valeur d'une «constante» peut vraiment avoir besoin d'être modifiée: par exemple, recharger ARGV à partir d'une boucle d'invite de type REPL, puis réexécuter ARGV via plus d'OptionParser.parse (suivant)! appels - voila! Donne aux "arguments de ligne de commande" un tout nouvel utilitaire dynamique.
Le problème pratique est soit avec l'hypothèse présumée que "ARGV doit être une constante", soit dans la propre méthode d'initialisation d'optparse, qui code en dur l'affectation d'ARGV à l'instance var @default_argv pour un traitement ultérieur - ce tableau (ARGV) devrait être un paramètre encourageant la ré-analyse et la réutilisation, le cas échéant. Un paramétrage correct, avec une valeur par défaut appropriée (par exemple, ARGV) éviterait de devoir jamais changer l'ARGV «constant». Juste quelques 2 ¢ dignes de réflexion ...
la source