Question Qu'est-ce que attr_accessor dans Ruby?


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


862
2017-12-06 21:07


origine


Réponses:


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 est appelé lecteur et ce dernier est appelé écrivain. Nous n'avons pas encore créé l'écrivain, 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. Maintenant, nous pouvons écrire et lire une variable d'instance @name en utilisant des méthodes de lecture et d'écriture. Sauf, 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. Lorsque vous voulez à la fois lecteur et écrivain, utilisez simplement l'accesseur!

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 personne, l'objet sera défini comme lorsque nous l'avons fait manuellement, donc vous pouvez 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 attr_reader, attr_writer, et attr_accessor méthodes réellement générer des méthodes pour vous, lisez d'autres réponses, des livres, ruby ​​docs.


2046
2017-12-06 22:11



attr_accessor est juste une méthode. (Le lien devrait fournir plus d'informations sur la façon dont 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 est pas une définition dans Ruby (c'est "juste une définition" dans des langages comme C ++ et Java), mais c'est un expression qui évalue. C'est lors de cette évaluation que le attr_accessor méthode invoquée qui à son tour modifie la classe actuelle - souvenez-vous du récepteur implicite: self.attr_accessor, où self est l'objet de classe "ouvert" à ce stade.

Le besoin de attr_accessor et amis, c'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 dans le x.y forme comme c'est commun dans, disons, Java ou même Python. En Ruby y est toujours considéré comme un message à envoyer (ou "méthode d'appel"). Ainsi, le attr_* méthodes créent des wrappers qui procèdent par proxy à l'instance @variable accès via des méthodes créées dynamiquement.

  2. Boilerplate suce

J'espère que cela clarifie certains des petits détails. Codage heureux.


1 Ce n'est pas strictement vrai et il y a quelques "techniques" autour de cette, mais il n'y a pas de support de syntaxe pour l'accès "variable d'instance publique".


114
2017-12-06 21:21



attr_accessor est (comme indiqué @pst) juste une méthode. Ce qu'il fait est de créer 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 vous-même ce genre de méthode dans 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

62
2017-12-06 21:29



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


35
2017-12-06 21:28



C'est juste une méthode qui définit les méthodes getter et setter pour les variables d'instance. Un exemple de mise en œuvre 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
2017-12-06 21:29



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


16
2017-12-06 21:11



J'ai aussi fait face à ce problème et j'ai écrit une réponse assez longue à cette question. Il y a déjà d'excellentes réponses à ce sujet, mais si vous voulez plus de précisions, j'espère que ma réponse peut aider

Initialiser la méthode

Initialize vous permet de définir des données sur une instance d'un objet lors de la création de l'instance plutôt que de devoir les définir sur une ligne distincte dans 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" en utilisant la méthode initialize en passant Dennis à travers le paramètre dans Initialize. Si nous voulions définir le nom sans la méthode initialize, 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 ens_accessor setter à 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 économise 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 setters (attr_reader (get), attr_writer (set), et attr_accessor (both)). Voir ci-dessous pour plus de détails sur ceux-ci.

Getters, Setters (attr_reader, attr_writer, attr_accessor)

Getters, attr_reader: L'objectif global d'un getter est de renvoyer la valeur d'une variable d'instance particulière. Visitez l'exemple de code ci-dessous pour 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 "item_name" et "quantity" sur l'instance de Item "example". Les "puts.name name" et "example.quantity" retournera (ou "get") la valeur pour les paramètres qui ont été transmis dans le "exemple" et les afficher à 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, seulement elle nous sauve six lignes de code. Imaginez si vous aviez 5 états supplémentaires attribuables à la classe Item? Le code deviendrait rapidement.

Setters, attr_writer: Ce qui m'a bloqué au début avec les méthodes de setter, c'est qu'à mes yeux, il semblait avoir une fonction identique à la méthode initialize. 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'un 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 où vous utiliseriez une méthode setter. C'EST LA DIFFÉRENCE. Vous n'avez pas besoin 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 item_name pour cette instance de la classe Item. Notez que nous continuons à utiliser la méthode getter attr_reader afin que nous puissions obtenir les valeurs et les imprimer à l'écran, juste au cas où vous voudriez tester le code par 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 encore une 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 où nous utilisons initialize pour définir la valeur des objets de item_name 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: Effectue les fonctions de attr_reader et attr_writer, en vous sauvant une ligne de code supplémentaire.


12
2017-09-16 01:03