O que é attr_accessor no Ruby?


1024

Estou tendo dificuldades para entender attr_accessorem Ruby .
Alguém pode me explicar isso?



1
Attr_accessor funciona da mesma maneira no Git? Estou descobrindo que alguns tutoriais não explicam o suficiente e outros assumem conhecimento prévio.
Angelfirenze

10
@Angelfirenze, gitnão tem nada a ver com attr_accessor. Git é um software de controle de versão, enquanto que attr_accessoré um método em Ruby .
Uzbekjon

Respostas:


2360

Digamos que você tenha uma aula Person.

class Person
end

person = Person.new
person.name # => no method error

Obviamente, nunca definimos método name. Vamos fazer isso.

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

Ah, podemos ler o nome, mas isso não significa que podemos atribuir o nome. Esses são dois métodos diferentes. O primeiro é chamado de leitor e o último é chamado de escritor . Ainda não criamos o escritor, então vamos fazer isso.

class Person
  def name
    @name
  end

  def name=(str)
    @name = str
  end
end

person = Person.new
person.name = 'Dennis'
person.name # => "Dennis"

Impressionante. Agora podemos escrever e ler variáveis ​​de instância @nameusando métodos reader e writer. Exceto que isso é feito com tanta frequência, por que perder tempo escrevendo esses métodos sempre? Nós podemos fazer isso mais fácil.

class Person
  attr_reader :name
  attr_writer :name
end

Mesmo isso pode se tornar repetitivo. Quando você quiser tanto leitor quanto escritor, use o acessador!

class Person
  attr_accessor :name
end

person = Person.new
person.name = "Dennis"
person.name # => "Dennis"

Funciona da mesma maneira! E adivinhe: a variável de instância @nameem nosso objeto pessoal será definida como quando a fizemos manualmente, para que você possa usá-la em outros métodos.

class Person
  attr_accessor :name

  def greeting
    "Hello #{@name}"
  end
end

person = Person.new
person.name = "Dennis"
person.greeting # => "Hello Dennis"

É isso aí. Para entender como attr_reader, attr_writere attr_accessormétodos realmente geram métodos para você, leia outras respostas, livros, documentos em ruby.


46
@hakunin - obrigado por essa resposta clara. O que está faltando para mim é por que a sintaxe Ruby sugere dois pontos ':' para as variáveis ​​de instância na instrução attr_ *? Parece que seria mais fácil usar a mesma sintaxe '@' usada em outros lugares da classe para se referir à variável.
Será

207
@WilliamSmith Para responder à sua pergunta, você precisa entender que attr_accessoré um método chamado na classe atual e :nameé um parâmetro que você passa para esse método. Não é uma sintaxe especial, é uma chamada de método simples. Se você desse uma @namevariável, não faria sentido, porque @name conteria nil. Então seria como escrever attr_accessor nil. Você não está passando uma variável que ela precisa criar, está passando o nome que deseja que a variável seja chamada.
Max Chernyak

23
@hakunin - Isso faz total sentido. Hoje eu aprendi que o ruby ​​está na verdade 'rodando' enquanto analisa um arquivo e que toda declaração e expressão é realmente uma chamada de método em algum objeto. Incluindo attr_accessor. Muito útil.
Será

52
Rails usado por 3 anos, nunca soube disso. Vergonha
Sean Xiao

5
@ Buminda sim, mas método namee variável @namenão são a mesma coisa. Não os confunda. Você tem uma variável de instância @nameem sua classe e define attr_reader :namepara poder lê-la de fora. Sem, attr_readernão há uma maneira simples de acessar @namefora da sua turma.
Max Chernyak

127

attr_accessor é apenas um método . (O link deve fornecer mais informações sobre como ele funciona - observe os pares de métodos gerados e um tutorial deve mostrar como usá-lo.)

O truque é que nãoclass é uma definição em Ruby (é "apenas uma definição" em linguagens como C ++ e Java), mas é uma expressão que avalia . É durante essa avaliação quando o attr_accessormétodo é chamado, que por sua vez modifica a classe atual - lembre-se do receptor implícito:, self.attr_accessoronde selfestá o objeto de classe "aberto" neste momento.

A necessidade de attr_accessor e os amigos são:

  1. Ruby, como Smalltalk, não permite que variáveis ​​de instância sejam acessadas fora dos métodos 1 para esse objeto. Ou seja, as variáveis ​​de instância não podem ser acessadas no x.yformulário, como é comum, por exemplo, Java ou mesmo Python. Em Ruby yé sempre tomada como uma mensagem para enviar (ou "método para chamar"). Assim, os attr_*métodos criam wrappers que @variableacessam a instância por meio de métodos criados dinamicamente.

  2. Boilerplate é uma merda

Espero que isso esclareça alguns dos pequenos detalhes. Feliz codificação.


1 Isso não é estritamente verdadeiro e existem algumas "técnicas" em torno disso , mas não há suporte à sintaxe para acesso à "variável de instância pública".


Quando você diz que attr_accessor é "apenas um método", eu entendi. Mas como é chamada a sintaxe usada para chamar esse método? Estou tendo problemas para encontrar a seção na documentação do ruby ​​que fala sobre sintaxe como some_method: name => "qualquer coisa",: notherName,: etc
BT

68

attr_accessoré (como @pst afirmou) apenas um método. O que ele faz é criar mais métodos para você.

Portanto, este código aqui:

class Foo
  attr_accessor :bar
end

é equivalente a este código:

class Foo
  def bar
    @bar
  end
  def bar=( new_value )
    @bar = new_value
  end
end

Você pode escrever esse tipo de método em 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

3
Este é um ótimo exemplo de onde a metaprogramação é usada mesmo nos cenários mais iniciantes. Muito agradável.
John Simon

2
Eu estava procurando por um esboço de implementação attr_accessore finalmente encontrei aqui! Embora tenha resolvido meu problema, mas estou curioso para saber onde (livro / documento oficial) posso encontrar um exemplo de implementação como este?
Wasif Hossain

40

attr_accessor é muito simples:

attr_accessor :foo

é um atalho para:

def foo=(val)
  @foo = val
end

def foo
  @foo
end

nada mais é do que um getter / setter para um objeto


10
sua resposta está bem. 'Atalho' significa "uma rota alternativa mais curta", de acordo com o meu dicionário, não "açúcar de sintaxe" ou "macro interpretada pelo intérprete".
bowsersenior

25

Basicamente, eles falsificam atributos de dados acessíveis ao público, que Ruby não possui.


4
Embora esse comentário não seja totalmente útil, é verdade. Destaca o fato de que os atributos de dados públicos não existem fora dos métodos "get" no Ruby, que são informações realmente úteis para alguém que tenta aprender o idioma.
quer

3
Isso realmente não deve ser rebaixado. Como um cara não-Ruby, tentando descobrir isso, essa resposta é muito útil!
19413 Brad

1
Concordado, parece muito semelhante ao nome do C # {get; set;}
David Miler

17

É apenas um método que define métodos getter e setter para variáveis ​​de instância. Um exemplo de implementação seria:

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

lidar com vários atributos dessa maneira é ótimo!
Wasif Hossain

Este foi um trecho de código realmente útil para resolver outra questão relacionada à metaprogramação.
alexventuraio 30/03

15

Explicação simples sem qualquer código

A maioria das respostas acima usa código. Esta explicação tenta responder sem usar nenhuma, por meio de uma analogia / história:

As partes externas não podem acessar segredos internos da CIA

  • Vamos imaginar um lugar realmente secreto: a CIA. Ninguém sabe o que está acontecendo na CIA além das pessoas dentro da CIA. Em outras palavras, pessoas externas não podem acessar nenhuma informação na CIA. Mas como não é bom ter uma organização que é completamente secreta, certas informações são disponibilizadas para o mundo exterior - apenas as coisas que a CIA deseja que todos saibam, é claro: por exemplo, o diretor da CIA, como esse departamento é ecológico para todos os outros departamentos governamentais etc. Outras informações: por exemplo, quem são seus agentes secretos no Iraque ou no Afeganistão - esse tipo de coisa provavelmente permanecerá em segredo pelos próximos 150 anos.

  • Se você estiver fora da CIA, poderá acessar apenas as informações disponibilizadas ao público. Ou, para usar a linguagem da CIA, você pode acessar apenas as informações "limpas".

  • As informações que a CIA deseja disponibilizar ao público em geral fora da CIA são denominadas: atributos.

O significado dos atributos de leitura e gravação:

  • No caso da CIA, a maioria dos atributos é "somente leitura". Isso significa que, se você é uma parte externa à CIA, pode perguntar: "quem é o diretor da CIA?" e você receberá uma resposta direta. Mas o que você não pode fazer com os atributos "somente leitura" é fazer alterações na CIA. por exemplo, você não pode fazer uma ligação e decidir subitamente que deseja que Kim Kardashian seja o diretor ou que Paris Hilton seja o comandante em chefe.

  • Se os atributos fornecerem acesso "de gravação", você poderá fazer alterações, se desejar, mesmo se estiver fora. Caso contrário, a única coisa que você pode fazer é ler.

    Em outras palavras, os acessadores permitem que você faça consultas ou faça alterações em organizações que de outra forma não permitem a entrada de pessoas externas, dependendo de os acessadores serem acessadores de leitura ou gravação.

Objetos dentro de uma classe podem acessar-se facilmente

  • Por outro lado, se você já estava dentro da CIA, poderia facilmente chamar seu agente da CIA em Cabul, porque essas informações são facilmente acessíveis, desde que você já esteja dentro. Mas se você estiver fora da CIA, você simplesmente não terá acesso: não poderá saber quem eles são (acesso de leitura) e não poderá alterar a missão deles (acesso de gravação).

Exatamente o mesmo com as classes e sua capacidade de acessar variáveis, propriedades e métodos dentro delas. HTH! Qualquer dúvida, por favor, pergunte e espero que eu possa esclarecer.


Sua explicação faz sentido! +1 Desculpe, você tem certeza de que a expressão "informações que são limpas pela CIA está correta?"
kouty 11/12/16

existem vários níveis de "liberação" na CIA: por exemplo, Top Secret (ninguém além do Prez) ou confiança pública (todos podem ler essa informação). A CIA realmente fornece muitos fatos muito interessantes!
BKSpurgeon

Você merece o voto positivo apenas pelos exemplos de Kardashian e Paris Hilton :) Eu pensei que era ruim o suficiente com Trump para Presidente, imagine aqueles dois encarregados!
Rmcsharry 24/03/19

Sim! É disso que precisamos, StackOverflow sem código! :-)
Marvin

13

Se você está familiarizado com o conceito de POO, deve familiarizar-se com o método getter e setter. attr_accessor faz o mesmo em Ruby.

Getter e Setter de maneira geral

class Person
  def name
    @name
  end

  def name=(str)
    @name = str
  end
end

person = Person.new
person.name = 'Eshaan'
person.name # => "Eshaan"

Método Setter

def name=(val)
  @name = val
end

Método Getter

def name
  @name
end

Método Getter e Setter em Ruby

class Person
  attr_accessor :name
end

person = Person.new
person.name = "Eshaan"
person.name # => "Eshaan"

2
explicação perfeita! É um comportamento muito útil e pode ser substituído com muita facilidade.
Rubyrider 8/15

12

Também enfrentei esse problema e escrevi uma resposta um tanto demorada para essa pergunta. Já existem ótimas respostas sobre isso, mas quem procura mais esclarecimentos, espero que minha resposta possa ajudar

Método de inicialização

Initialize permite que você defina dados para uma instância de um objeto após a criação da instância, em vez de precisar defini-los em uma linha separada no seu código toda vez que você cria uma nova instância da classe.

class Person

  def initialize(name)
    @name = name
  end


  def greeting
    "Hello #{@name}"
  end
end

person = Person.new("Denis")
puts person.greeting

No código acima, estamos definindo o nome “Denis” usando o método initialize passando Dennis pelo parâmetro em Initialize. Se quiséssemos definir o nome sem o método initialize, poderíamos fazer o seguinte:

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

No código acima, definimos o nome chamando o método attr_accessor setter usando person.name, em vez de definir os valores na inicialização do objeto.

Os dois "métodos" de fazer esse trabalho, mas inicializar, economizam tempo e linhas de código.

Este é o único trabalho de inicialização. Você não pode chamar a inicialização como um método. Para realmente obter os valores de um objeto de instância, você precisa usar getters e setters (attr_reader (get), attr_writer (set) e attr_accessor (ambos)). Veja abaixo mais detalhes sobre esses.

Getters, Setters (attr_reader, attr_writer, attr_accessor)

Getters, attr_reader: O objetivo inteiro de um getter é retornar o valor de uma variável de instância específica. Visite o código de exemplo abaixo para obter detalhes sobre isso.

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

No código acima, você está chamando os métodos "item_name" e "quantidade" na instância do item "exemplo". O “puts example.item_name” e “example.quantity” retornarão (ou “obterão”) o valor dos parâmetros que foram passados ​​para o “exemplo” e os exibirão na tela.

Felizmente, em Ruby, existe um método inerente que nos permite escrever esse código de forma mais sucinta; o método attr_reader. Veja o código abaixo;

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

Essa sintaxe funciona exatamente da mesma maneira, mas nos salva seis linhas de código. Imagine se você tivesse mais 5 estados atribuíveis à classe Item? O código iria demorar muito rapidamente.

Setters, attr_writer: O que me impressionou no início com os métodos setter é que, aos meus olhos, parecia desempenhar uma função idêntica ao método inicializar. Abaixo, explico a diferença com base no meu entendimento;

Como mencionado anteriormente, o método initialize permite definir os valores para uma instância de um objeto na criação do objeto.

Mas e se você quiser definir os valores posteriormente, após a instância ter sido criada, ou alterá-los após a inicialização? Este seria um cenário em que você usaria um método setter. Essa é a diferença. Você não precisa "definir" um estado específico quando estiver usando o método attr_writer inicialmente.

O código abaixo é um exemplo de uso de um método setter para declarar o valor item_name para esta instância da classe Item. Observe que continuamos a usar o método getter attr_reader para que possamos obter os valores e imprimi-los na tela, caso você queira testar o código por conta própria.

class Item

attr_reader :item_name

  def item_name=(str)
    @item_name = (str)
  end

end

O código abaixo é um exemplo de uso do attr_writer para encurtar novamente nosso código e economizar tempo.

class Item

attr_reader :item_name
attr_writer :item_name

end

item = Item.new
puts item.item_name = "TV"

O código abaixo é uma reiteração do exemplo de inicialização acima de onde estamos usando initialize para definir o valor dos objetos item_name na criação.

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: Executa as funções de attr_reader e attr_writer, economizando mais uma linha de código.


10

Eu acho que parte do que confunde novos rubiistas / programadores (como eu) é:

"Por que não posso simplesmente dizer à instância que ele possui algum atributo (por exemplo, nome) e atribuir um valor a esse atributo de uma só vez?"

Um pouco mais generalizado, mas foi assim que clicou para mim:

Dado:

class Person
end

Não definimos Person como algo que pode ter um nome ou qualquer outro atributo para esse assunto.

Então, se nós:

baby = Person.new

... e tente dar um nome a eles ...

baby.name = "Ruth"

Recebemos um erro porque, em Rubyland, uma classe de objeto Person não é algo associado ou capaz de ter um "nome" ... ainda!

MAS podemos usar qualquer um dos métodos fornecidos (consulte as respostas anteriores) como uma maneira de dizer: "Uma instância de uma classe Person ( baby) agora pode ter um atributo chamado 'name'; portanto, não temos apenas uma maneira sintática de obter e definindo esse nome, mas faz sentido para nós ".

Novamente, respondendo a essa pergunta de um ângulo um pouco diferente e mais geral, mas espero que isso ajude a próxima instância da classe Person que encontra seu caminho para esse segmento.


7

Simplificando, ele definirá um setter e um getter para a classe.

Observe que

attr_reader :v is equivalant to 
def v
  @v
end

attr_writer :v is equivalant to
def v=(value)
  @v=value
end

assim

attr_accessor :v which means 
attr_reader :v; attr_writer :v 

são equivalentes para definir um setter e um getter para a classe.


5

Simplesmente attr-accessorcria os métodos gettere setterpara os atributos especificados


5

Outra maneira de entender isso é descobrir qual código de erro ele elimina por ter attr_accessor .

Exemplo:

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

Os seguintes métodos estão disponíveis:

$ bankie = BankAccout.new("Iggy")
$ bankie 
$ bankie.deposit(100)
$ bankie.withdraw(5)

Os seguintes métodos geram erro:

$ bankie.owner     #undefined method `owner'... 
$ bankie.balance   #undefined method `balance'...

ownere balancenão são, tecnicamente, um método , mas um atributo. A classe BankAccount não possui def ownere def balance. Nesse caso, você pode usar os dois comandos abaixo. Mas esses dois métodos não estão lá. No entanto, você pode acessar atributos como se você tivesse acessar um método via attr_accessor!! Daí a palavraattr_accessor . Atributo. Accessor. Ele acessa atributos como você acessaria um método.

A adição attr_accessor :balance, :ownerpermite ler e escrever balancee owner"método". Agora você pode usar os dois últimos métodos.

$ bankie.balance
$ bankie.owner

2

Define um atributo nomeado para este módulo, em que o nome é symbol.id2name, criando uma variável de instância (@name) e um método de acesso correspondente para lê-lo. Também cria um método chamado name = para definir o atributo.

module Mod
  attr_accessor(:one, :two)
end
Mod.instance_methods.sort   #=> [:one, :one=, :two, :two=]

1

Resumir um acessador de atributo, também conhecido como attr_accessor, oferece dois métodos gratuitos.

Como em Java, eles são chamados de getters e setters.

Muitas respostas mostraram bons exemplos, por isso vou ser breve.

#the_attribute

e

# the_attribute =

Nos documentos antigos do ruby, um hash tag # significa um método. Também pode incluir um prefixo de nome de classe ... MyClass # my_method


1

Eu sou novo no Ruby e tive que lidar apenas com a compreensão da seguinte estranheza. Pode ajudar alguém no futuro. No final, é como foi mencionado acima, onde 2 funções (def myvar, def myvar =) ficam implicitamente para acessar @myvar, mas esses métodos podem ser substituídos por declarações locais.

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

Atributos e métodos de acessador

Atributos são componentes de classe que podem ser acessados ​​de fora do objeto. Eles são conhecidos como propriedades em muitas outras linguagens de programação. Seus valores são acessíveis usando a "notação de ponto", como em object_name.attribute_name. Diferentemente do Python e de algumas outras linguagens, o Ruby não permite que variáveis ​​da instância sejam acessadas diretamente de fora do objeto.

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>

No exemplo acima, c é uma instância (objeto) da classe Car. Tentamos, sem sucesso, ler o valor da variável de instância wheel de fora do objeto. O que aconteceu foi que Ruby tentou chamar um método chamado wheel dentro do objeto c, mas nenhum método foi definido. Em resumo, object_name.attribute_name tenta chamar um método chamado attribute_name dentro do objeto. Para acessar o valor da variável wheel de fora, precisamos implementar um método de instância com esse nome, que retornará o valor dessa variável quando for chamado. Isso é chamado de método acessador. No contexto geral de programação, a maneira usual de acessar uma variável de instância de fora do objeto é implementar métodos acessadores, também conhecidos como métodos getter e setter.

No exemplo a seguir, adicionamos métodos getter e setter à classe Car para acessar a variável wheel de fora do objeto. Esta não é a "maneira Ruby" de definir getters e setters; serve apenas para ilustrar o que os métodos getter e setter fazem.

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

O exemplo acima funciona e um código semelhante é comumente usado para criar métodos getter e setter em outros idiomas. No entanto, o Ruby fornece uma maneira mais simples de fazer isso: três métodos internos chamados attr_reader, attr_writer e attr_acessor. O método attr_reader torna uma variável de instância legível do lado de fora, attr_writer a torna gravável e attr_acessor a torna legível e gravável.

O exemplo acima pode ser reescrito assim.

class Car
  attr_accessor :wheels
end

f = Car.new
f.wheels = 4
f.wheels  # Output: => 4

No exemplo acima, o atributo wheel será legível e gravável de fora do objeto. Se, em vez de attr_accessor, usamos attr_reader, seria somente leitura. Se usássemos attr_writer, seria somente gravação. Esses três métodos não são getters e setters em si mesmos, mas, quando chamados, criam métodos getter e setter para nós. São métodos que geram dinamicamente (programaticamente) outros métodos; isso é chamado de metaprogramação.

O primeiro exemplo (mais longo), que não emprega os métodos internos do Ruby, deve ser usado apenas quando código adicional for necessário nos métodos getter e setter. Por exemplo, um método setter pode precisar validar dados ou fazer algum cálculo antes de atribuir um valor a uma variável de instância.

É possível acessar (ler e escrever) variáveis ​​de instância de fora do objeto, usando os métodos internos instance_variable_get e instance_variable_set. No entanto, isso raramente é justificável e geralmente é uma péssima idéia, já que contornar o encapsulamento tende a causar todo tipo de confusão.


-2

Hummm. Muitas boas respostas. Aqui estão meus poucos centavos.

  • attr_accessoré um método simples que nos ajuda a limpar ( secar ) os métodos repetidosgetter and setter .

  • Para que possamos nos concentrar mais em escrever a lógica de negócios e não nos preocupar com os levantadores e os levantadores.


-3

A principal funcionalidade do attr_accessor sobre as outras é a capacidade de acessar dados de outros arquivos.
Então você normalmente teria attr_reader ou attr_writer, mas a boa notícia é que Ruby permite combinar esses dois com o attr_accessor. Penso nisso como o meu método de ir, porque é mais bem-arredondado ou versátil. Além disso, lembre-se de que, no Rails, isso é eliminado porque faz isso por você no back-end. Portanto, em outras palavras: é melhor usar o attr_acessor em relação aos outros dois porque não precisa se preocupar em ser específico, o acessador cobre tudo. Eu sei que essa é uma explicação mais geral, mas me ajudou como iniciante.

Espero que isso tenha ajudado!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.