J'ai du mal à comprendre attr_accessor
en Ruby. Quelqu'un peut m'expliquer cela?
Disons que vous avez une classe Person
.
class Person
end
person = Person.new
person.name # => no method error
De toute évidence, nous n'avons jamais défini la méthode name
. Faisons cela.
class Person
def name
@name # simply returning an instance variable @name
end
end
person = Person.new
person.name # => nil
person.name = "Dennis" # => no method error
Aha, nous pouvons lire le nom, mais cela ne signifie pas que nous pouvons attribuer le nom. Ce sont deux méthodes différentes. Le premier s'appelle lecteur et le dernier s'appelle écrivain . Nous n'avons pas encore créé l'auteur, alors faisons-le.
class Person
def name
@name
end
def name=(str)
@name = str
end
end
person = Person.new
person.name = 'Dennis'
person.name # => "Dennis"
Impressionnant. Nous pouvons maintenant écrire et lire la variable d'instance @name
à l'aide des méthodes de lecture et d'écriture. Sauf que cela se fait si souvent, pourquoi perdre du temps à écrire ces méthodes à chaque fois? Nous pouvons le faire plus facilement.
class Person
attr_reader :name
attr_writer :name
end
Même cela peut devenir répétitif. Quand vous voulez à la fois lecteur et écrivain, utilisez simplement accessor!
class Person
attr_accessor :name
end
person = Person.new
person.name = "Dennis"
person.name # => "Dennis"
Fonctionne de la même manière! Et devinez quoi: la variable d’instance @name
dans notre objet personne sera définie comme lorsque nous l’avions faite manuellement, vous pouvez donc l’utiliser dans d’autres méthodes.
class Person
attr_accessor :name
def greeting
"Hello #{@name}"
end
end
person = Person.new
person.name = "Dennis"
person.greeting # => "Hello Dennis"
C'est tout. Afin de comprendre comment les méthodes attr_reader
, attr_writer
et attr_accessor
génèrent des méthodes pour vous, lisez d'autres réponses, livres, ruby docs.
(attr_accessor } _ est juste une méthode. (Le lien devrait permettre de mieux comprendre comment cela fonctionne. Regardez les paires de méthodes générées et un tutoriel devrait vous montrer comment l'utiliser.)
L'astuce est que class
n'est pas une définition en Ruby (c'est "juste une définition" dans des langages tels que C++ et Java), mais c'est une expression qui évalue. C'est pendant cette évaluation que la méthode attr_accessor
est invoquée, ce qui modifie à son tour la classe actuelle. Rappelez-vous le destinataire implicite: self.attr_accessor
, où self
est l'objet de classe "ouvert" à ce stade.
Le besoin de attr_accessor
et d'amis, est, bien:
Ruby, comme Smalltalk, n'autorise pas l'accès aux variables d'instance en dehors des méthodes1 pour cet objet. Autrement dit, les variables d'instance ne sont pas accessibles sous la forme x.y
, comme c'est souvent le cas dans Java ou même Python. En Ruby, y
est toujours considéré comme un message à envoyer (ou "méthode à appeler"). Ainsi, les méthodes attr_*
créent des wrappers qui proxy l'accès de l'instance @variable
par le biais de méthodes créées dynamiquement.
Plaque de cuisson suce
J'espère que cela clarifie certains des petits détails. Bonne codage.
1 Ce n'est pas tout à fait vrai et il y a quelques "techniques" autour de cela , mais la syntaxe n'est pas prise en charge pour l'accès "variable d'instance publique".
attr_accessor
est (comme @pst indiqué) juste une méthode. Cela crée plus de méthodes pour vous.
Donc ce code ici:
class Foo
attr_accessor :bar
end
est équivalent à ce code:
class Foo
def bar
@bar
end
def bar=( new_value )
@bar = new_value
end
end
Vous pouvez écrire ce genre de méthode vous-même en Ruby:
class Module
def var( method_name )
inst_variable_name = "@#{method_name}".to_sym
define_method method_name do
instance_variable_get inst_variable_name
end
define_method "#{method_name}=" do |new_value|
instance_variable_set inst_variable_name, new_value
end
end
end
class Foo
var :bar
end
f = Foo.new
p f.bar #=> nil
f.bar = 42
p f.bar #=> 42
attr_accessor
est très simple:
attr_accessor :foo
est un raccourci pour:
def foo=(val)
@foo = val
end
def foo
@foo
end
ce n'est rien de plus qu'un getter/setter pour un objet
Fondamentalement, ils simulent des attributs de données accessibles au public, ce que Ruby n'a pas.
C'est juste une méthode qui définit les méthodes getter et setter pour les variables d'instance. Un exemple d'implémentation serait:
def self.attr_accessor(*names)
names.each do |name|
define_method(name) {instance_variable_get("@#{name}")} # This is the getter
define_method("#{name}=") {|arg| instance_variable_set("@#{name}", arg)} # This is the setter
end
end
J'ai également fait face à ce problème et ai écrit une réponse un peu longue à cette question. Il y a déjà d'excellentes réponses à ce sujet, mais tous ceux qui recherchent des éclaircissements peuvent espérer que ma réponse pourra aider.
Méthode d'initialisation
Initialiser vous permet de définir des données sur une instance d'un objet lors de la création de l'instance, sans avoir à les définir sur une ligne distincte de votre code chaque fois que vous créez une nouvelle instance de la classe.
class Person
attr_accessor :name
def initialize(name)
@name = name
end
def greeting
"Hello #{@name}"
end
end
person = Person.new("Denis")
puts person.greeting
Dans le code ci-dessus, nous définissons le nom «Denis» à l'aide de la méthode initialize en transmettant Dennis via le paramètre dans Initialize. Si nous voulions définir le nom sans la méthode d'initialisation, nous pourrions le faire comme ceci:
class Person
attr_accessor :name
# def initialize(name)
# @name = name
# end
def greeting
"Hello #{name}"
end
end
person = Person.new
person.name = "Dennis"
puts person.greeting
Dans le code ci-dessus, nous définissons le nom en appelant la méthode de définition attr_accessor à l'aide de person.name, plutôt que de définir les valeurs lors de l'initialisation de l'objet.
Les deux «méthodes» de faire ce travail, mais initialiser nous fait gagner du temps et des lignes de code.
C'est le seul travail d'initialisation. Vous ne pouvez pas appeler initialize en tant que méthode. Pour obtenir réellement les valeurs d'un objet d'instance, vous devez utiliser des getters et des setters (attr_reader (get), attr_writer (set) et attr_accessor (les deux)). Voir ci-dessous pour plus de détails sur ceux-ci.
Getters, Setters (attr_reader, attr_writer, attr_accessor)
Getters, attr_reader: Le but d'un getter est de renvoyer la valeur d'une variable d'instance particulière. Visitez l'exemple de code ci-dessous pour obtenir une ventilation à ce sujet.
class Item
def initialize(item_name, quantity)
@item_name = item_name
@quantity = quantity
end
def item_name
@item_name
end
def quantity
@quantity
end
end
example = Item.new("TV",2)
puts example.item_name
puts example.quantity
Dans le code ci-dessus, vous appelez les méthodes “nom_élément” et “quantité” sur l'instance de l'élément “exemple”. Les expressions "met exemple.nom_article" et "exemple.quantité" renverront (ou "obtiendront") la valeur des paramètres passés dans "exemple" et les afficheront à l'écran.
Heureusement, dans Ruby, il existe une méthode inhérente qui nous permet d’écrire ce code plus succinctement; la méthode attr_reader. Voir le code ci-dessous;
class Item
attr_reader :item_name, :quantity
def initialize(item_name, quantity)
@item_name = item_name
@quantity = quantity
end
end
item = Item.new("TV",2)
puts item.item_name
puts item.quantity
Cette syntaxe fonctionne exactement de la même manière, si ce n’est qu’elle nous enregistre six lignes de code. Imaginez si vous aviez 5 autres états attribuables à la classe Item? Le code deviendrait long rapidement.
Setters, attr_writer: Ce qui m'a échappé au début avec les méthodes de définition, c'est qu'à mes yeux, cette fonction semblait remplir la même fonction que la méthode d'initialisation. Ci-dessous, j'explique la différence en fonction de ma compréhension.
Comme indiqué précédemment, la méthode initialize vous permet de définir les valeurs d'une instance d'objet lors de sa création.
Mais que se passe-t-il si vous souhaitez définir les valeurs ultérieurement, après la création de l'instance, ou les modifier après leur initialisation? Ce serait un scénario dans lequel vous utiliseriez une méthode de définition. CELA IS LA DIFFERENCE. Il n'est pas nécessaire de "définir" un état particulier lorsque vous utilisez initialement la méthode attr_writer.
Le code ci-dessous est un exemple d'utilisation d'une méthode setter pour déclarer la valeur nom_élément de cette instance de la classe Item. Notez que nous continuons à utiliser la méthode getter attr_reader afin d’obtenir les valeurs et de les imprimer à l’écran, au cas où vous voudriez tester le code vous-même.
class Item
attr_reader :item_name
def item_name=(str)
@item_name = (str)
end
end
Le code ci-dessous est un exemple d'utilisation de attr_writer pour raccourcir une nouvelle fois notre code et nous faire gagner du temps.
class Item
attr_reader :item_name
attr_writer :item_name
end
item = Item.new
puts item.item_name = "TV"
Le code ci-dessous est une réitération de l'exemple d'initialisation ci-dessus indiquant où nous utilisons initialize pour définir la valeur des objets nom_élément lors de la création.
class Item
attr_reader :item_name
def initialize(item_name)
@item_name = item_name
end
end
item = Item.new("TV")
puts item.item_name
attr_accessor: exécute les fonctions d'attr_reader et d'attr_writer, en vous épargnant une ligne de code supplémentaire.
Si vous connaissez le concept OOP, vous devez connaître la méthode d’acquisition et de définition de la méthode . Attr_accessor fait de même dans Ruby.
Getter et Setter de manière générale
class Person
def name
@name
end
def name=(str)
@name = str
end
end
person = Person.new
person.name = 'Eshaan'
person.name # => "Eshaan"
Méthode Setter
def name=(val)
@name = val
end
Méthode Getter
def name
@name
end
Méthode Getter et Setter en Ruby
class Person
attr_accessor :name
end
person = Person.new
person.name = "Eshaan"
person.name # => "Eshaan"
Je pense qu'une partie de la confusion chez les nouveaux rubyistes/programmeurs (comme moi) est la suivante:
"Pourquoi ne puis-je pas simplement dire à l'instance qu'elle a un attribut donné (par exemple, un nom) et lui donner une valeur en un seul coup?"
Un peu plus généralisé, mais voici comment cela a cliqué pour moi:
Donné:
class Person
end
Nous n'avons pas défini Person comme quelque chose qui peut avoir un nom ou tout autre attribut.
Donc si nous:
baby = Person.new
... et essayez de leur donner un nom ...
baby.name = "Ruth"
Nous obtenons un error parce que, dans Rubyland, une classe d'objet Person n'est pas quelque chose qui est associé à ou susceptible d'avoir un "nom" ... pour le moment!
MAIS nous pouvons utiliser n’importe laquelle des méthodes données (voir les réponses précédentes) comme moyen de dire: "Une instance d’une classe Person (baby
) peut maintenant avoir un attribut appelé" nom "; manière syntaxique d’obtenir et de définir ce nom, mais il est logique que nous le fassions. "
Encore une fois, aborder cette question sous un angle légèrement différent et plus général, mais j'espère que cela aidera l'instance suivante de la classe Person qui trouve son chemin vers ce fil.
En termes simples, il définira un passeur et un getter pour la classe.
Notez que
attr_reader :v is equivalant to
def v
@v
end
attr_writer :v is equivalant to
def v=(value)
@v=value
end
Alors
attr_accessor :v which means
attr_reader :v; attr_writer :v
sont équivalents pour définir un passeur et un getter pour la classe.
attr-accessor
crée simplement les méthodes getter
et setter
pour les attributs spécifiés
Une autre façon de le comprendre est de déterminer le code d'erreur éliminé en ayant attr_accessor
.
Exemple:
class BankAccount
def initialize( account_owner )
@owner = account_owner
@balance = 0
end
def deposit( amount )
@balance = @balance + amount
end
def withdraw( amount )
@balance = @balance - amount
end
end
Les méthodes suivantes sont disponibles:
$ bankie = BankAccout.new("Iggy")
$ bankie
$ bankie.deposit(100)
$ bankie.withdraw(5)
Les méthodes suivantes génèrent une erreur:
$ bankie.owner #undefined method `owner'...
$ bankie.balance #undefined method `balance'...
owner
et balance
ne sont techniquement pas une méthode, mais un attribut. La classe BankAccount n'a pas def owner
et def balance
. Si tel est le cas, vous pouvez utiliser les deux commandes ci-dessous. Mais ces deux méthodes ne sont pas là. Cependant, vous pouvez accéder aux attributs comme si vous souhaitiez accéder à une méthode via attr_accessor
!! D'où le mot attr_accessor
. Attribut. Accesseur. Il accède aux attributs comme vous accéderiez à une méthode.
Ajouter attr_accessor :balance, :owner
vous permet de lire et d’écrire balance
et owner
"méthode". Vous pouvez maintenant utiliser les 2 dernières méthodes.
$ bankie.balance
$ bankie.owner
Définit un attribut nommé pour ce module, dont le nom est symbol.id2name, créant une variable d'instance (@name) et une méthode d'accès correspondante pour la lire. Crée également une méthode appelée name = pour définir l'attribut.
module Mod
attr_accessor(:one, :two)
end
Mod.instance_methods.sort #=> [:one, :one=, :two, :two=]
Pour résumer un accesseur d'attribut aka attr_accessor vous donne deux méthodes gratuites.
Comme à Java, ils se font appeler Getters et Setters.
De nombreuses réponses ont montré de bons exemples, je vais donc être bref.
#le_attribut
et
# the_attribute =
Dans les anciens documents Ruby, une balise de hachage # désigne une méthode . Elle pourrait également inclure un préfixe de nom de classe ...
Je suis nouvelle chez Ruby et je n’avais qu’à comprendre la bizarrerie suivante. Peut aider quelqu'un d'autre à l'avenir. En fin de compte, c'est comme cela a été mentionné précédemment, où 2 fonctions (def myvar, def myvar =) obtiennent toutes deux implicitement l'accès à @myvar, mais ces méthodes peuvent être remplacées par des déclarations locales.
class Foo
attr_accessor 'myvar'
def initialize
@myvar = "A"
myvar = "B"
puts @myvar # A
puts myvar # B - myvar declared above overrides myvar method
end
def test
puts @myvar # A
puts myvar # A - coming from myvar accessor
myvar = "C" # local myvar overrides accessor
puts @myvar # A
puts myvar # C
send "myvar=", "E" # not running "myvar =", but instead calls setter for @myvar
puts @myvar # E
puts myvar # C
end
end
Les attributs sont des composants de classe auxquels on peut accéder de l'extérieur de l'objet. Ils sont connus comme propriétés dans de nombreux autres langages de programmation. Leurs valeurs sont accessibles en utilisant la "notation par points", comme dans nom_objet.nom_attribut. Contrairement à Python et à quelques autres langages, Ruby n'autorise pas l'accès direct aux variables d'instance depuis l'extérieur de l'objet.
class Car
def initialize
@wheels = 4 # This is an instance variable
end
end
c = Car.new
c.wheels # Output: NoMethodError: undefined method `wheels' for #<Car:0x00000000d43500>
Dans l'exemple ci-dessus, c est une instance (objet) de la classe Car. Nous avons essayé sans succès de lire la valeur de la variable d'instance wheel de l'extérieur de l'objet. Ce qui s’est passé, c’est que Ruby a tenté d’appeler une méthode nommée wheel dans l’objet c, mais aucune méthode de ce type n’a été définie. En bref, nom_objet.nom_attribut tente d'appeler une méthode nommée nom_attribut dans l'objet. Pour accéder à la valeur de la variable wheel de l'extérieur, nous devons implémenter une méthode d'instance portant ce nom, qui renverra la valeur de cette variable lorsqu'elle sera appelée. Cela s'appelle une méthode d'accès. Dans le contexte général de la programmation, la méthode habituelle pour accéder à une variable d'instance depuis l'extérieur de l'objet consiste à implémenter des méthodes d'accès, également appelées méthodes getter et setter. Un getter permet à la valeur d'une variable définie dans une classe d'être lue de l'extérieur et un séparateur permet son écriture de l'extérieur.
Dans l'exemple suivant, nous avons ajouté des méthodes getter et setter à la classe Car pour accéder à la variable wheel de l'extérieur de l'objet. Ce n'est pas la "manière Ruby" de définir les getters et les setters; cela ne sert qu'à illustrer ce que font les méthodes getter et setter.
class Car
def wheels # getter method
@wheels
end
def wheels=(val) # setter method
@wheels = val
end
end
f = Car.new
f.wheels = 4 # The setter method was invoked
f.wheels # The getter method was invoked
# Output: => 4
L'exemple ci-dessus fonctionne et un code similaire est couramment utilisé pour créer des méthodes getter et setter dans d'autres langues. Cependant, Ruby offre un moyen plus simple de procéder: trois méthodes intégrées, appelées attr_reader, attr_writer et attr_acessor. La méthode attr_reader rend une variable d'instance lisible de l'extérieur, attr_writer la rend accessible en écriture et attr_acessor la rend lisible et accessible en écriture.
L'exemple ci-dessus peut être réécrit comme ceci.
class Car
attr_accessor :wheels
end
f = Car.new
f.wheels = 4
f.wheels # Output: => 4
Dans l'exemple ci-dessus, l'attribut wheel sera lisible et inscriptible de l'extérieur de l'objet. Si au lieu de attr_accessor, nous utilisions attr_reader, ce serait en lecture seule. Si nous utilisions attr_writer, ce serait en écriture seule. Ces trois méthodes ne sont pas des geters et des setters en elles-mêmes mais, lorsqu'elles sont appelées, elles créent des méthodes getter et setter pour nous. Ce sont des méthodes qui génèrent (par programmation) d’autres méthodes; c'est ce qu'on appelle la métaprogrammation.
Le premier exemple (plus long), qui n'utilise pas les méthodes intégrées de Ruby, ne doit être utilisé que lorsqu'un code supplémentaire est requis dans les méthodes d'accesseur et de sélecteur. Par exemple, une méthode de définition peut avoir besoin de valider des données ou d'effectuer des calculs avant d'attribuer une valeur à une variable d'instance.
Il est possible d'accéder (en lecture et en écriture) aux variables d'instance de l'extérieur de l'objet, en utilisant les méthodes intégrées instance_variable_get et instance_variable_set. Cependant, cela est rarement justifiable et généralement une mauvaise idée, car contourner l’encapsulation tend à causer toutes sortes de dégâts.