web-dev-qa-db-fra.com

Qu'est-ce que attr_accessor en Ruby?

J'ai du mal à comprendre attr_accessor en Ruby. Quelqu'un peut m'expliquer cela?

923
dennismonsewicz

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. 

2156
Max Chernyak

(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:

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

  2. 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".

117
user166390

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
64
Phrogz

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

36
efalcao

Fondamentalement, ils simulent des attributs de données accessibles au public, ce que Ruby n'a pas.

19
Tyler Eaves

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
17
Chuck

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.

12
Jbur43

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"
11
Ahmed 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.

10
Ben

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.

7
Marcus Thornton

attr-accessor crée simplement les méthodes getter et setter pour les attributs spécifiés

5
Veeru

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
4
Iggy

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=]
2
Praveen_Shukla

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

1
Douglas G. Allen

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
0
Adverbly

Attributs et méthodes d'accès

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.

0
BrunoFacca