Что такое attr_accessor в Ruby?

872

Мне трудно понять attr_accessor в Ruby. Может кто-нибудь объяснить это мне?

  • 18
    Пожалуйста, смотрите также Зачем использовать Ruby's attr_accessor, attr_reader и attr_writer? ,
  • 1
    Работает ли attr_accessor таким же образом в Git? Я обнаружил, что некоторые учебные пособия не объясняют достаточно, а другие предполагают предварительные знания.
Показать ещё 1 комментарий
Теги:

20 ответов

2239
Лучший ответ

Скажем, у вас есть класс Person.

class Person
end

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

Очевидно, мы никогда не определяли метод name. Позвольте сделать это.

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

Ага, мы можем прочитать имя, но это не значит, что мы можем назначить имя. Это два разных метода. Первый называется читателем, а последний называется писателем. Мы еще не создали автора, поэтому давайте сделаем это.

class Person
  def name
    @name
  end

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

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

Высокий. Теперь мы можем написать и прочитать переменную экземпляра @name, используя методы чтения и записи. Кроме того, это делается так часто, зачем тратить время на эти методы каждый раз? Мы можем сделать это проще.

class Person
  attr_reader :name
  attr_writer :name
end

Даже это может стать повторяющимся. Когда вы хотите, чтобы читатель и писатель просто использовали accessor!

class Person
  attr_accessor :name
end

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

Работает одинаково! И угадайте, что: переменная экземпляра @name в нашем объекте человека будет установлена ​​так же, как когда мы сделали это вручную, поэтому вы можете использовать его в других методах.

class Person
  attr_accessor :name

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

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

Что это. Чтобы понять, как методы attr_reader, attr_writer и attr_accessor действительно генерируют методы для вас, читайте другие ответы, книги, рубиновые документы.

  • 42
    @hakunin - спасибо за этот четкий ответ. Что мне не хватает, так это то, что синтаксис Ruby предлагает использовать двоеточие ':' для переменных экземпляра в выражении attr_ *? Кажется, что было бы проще использовать тот же синтаксис '@', который используется в других местах класса для ссылки на переменную.
  • 192
    @WilliamSmith Чтобы ответить на ваш вопрос, вы должны понимать, что attr_accessor - это метод, вызываемый в текущем классе, а :name - это параметр, который вы передаете этому методу. Это не специальный синтаксис, это простой вызов метода. Если бы вы дали ей переменную @name , это не имело бы смысла, потому что @name содержало бы nil . Так что это будет похоже на запись attr_accessor nil . Вы не передаете ему переменную, которую нужно создать, вы передаете имя, которое хотите, чтобы переменная была вызвана.
Показать ещё 29 комментариев
112

attr_accessor - всего лишь метод. (Ссылка должна дать больше информации о том, как она работает - посмотрите на пары генерируемых методов, а в учебнике должно быть показано, как ее использовать.)

Фокус в том, что class не является определением в Ruby (это "просто определение" в таких языках, как С++ и Java), но это выражение, которое оценивается. Именно во время этой оценки, когда вызывается метод attr_accessor, который, в свою очередь, изменяет текущий класс - помните неявный получатель: self.attr_accessor, где self - это объект открытого объекта в этой точке.

Необходимость attr_accessor и друзей, есть, хорошо:

  • Ruby, подобно Smalltalk, не позволяет обращаться к переменным экземпляра вне методов 1 для этого объекта. То есть, переменные экземпляра не могут быть доступны в форме x.y, как это принято говорить, Java или даже Python. В Ruby y всегда используется как сообщение для отправки (или "метод для вызова" ). Таким образом, методы attr_* создают оболочки, которые проксируют доступ экземпляра @variable к динамически созданным методам.

  • Бойлер всасывает

Надеемся, что это разъяснит некоторые детали. Счастливое кодирование.


1 Это не совсем так, и есть некоторые "методы" вокруг этого, но нет поддержки синтаксиса для "public переменная экземпляра".

  • 7
    +1 за объяснение семантики!
  • 0
    Когда вы говорите, что attr_accessor - «просто метод», я получаю это. Но какой синтаксис используется для вызова этого метода? У меня возникают проблемы с поиском раздела в документации по ruby, в котором говорится о синтаксисе, например some_method: name => "what",: notherName,: etc
61

attr_accessor является (как указано в @pst) просто методом. Что он делает, так это создавать больше методов для вас.

Итак, этот код здесь:

class Foo
  attr_accessor :bar
end

эквивалентен этому коду:

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

Вы можете написать этот метод самостоятельно в 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
  • 12
    @bowsersenior вы можете быть правы, но это наверняка помогло мне.
  • 2
    Это отличный пример того, как метапрограммирование используется даже в сценариях самого начального уровня. Очень хорошо.
Показать ещё 1 комментарий
36

attr_accessor очень просто:

attr_accessor :foo

является ярлыком для:

def foo=(val)
  @foo = val
end

def foo
  @foo
end

это не что иное, как геттер/сеттер для объекта

  • 1
    Называть его «ярлыком» звучит так, как будто это синтаксический сахар или какая-то форма макроса, расширенная интерпретатором (вместо метода, которым он на самом деле является).
  • 10
    ваш ответ в порядке. «Мой ярлык» означает «более короткий альтернативный маршрут» согласно моему словарю, а не «синтаксический сахар» или «макрос, интерпретируемый интерпретатором».
18

В основном они поддельные общедоступные атрибуты данных, которые Ruby не имеет.

  • 3
    Хотя этот комментарий не совсем полезен, это правда. Подчеркивает тот факт, что общедоступные атрибуты данных не существуют вне методов get в Ruby, что является действительно полезной информацией для тех, кто пытается изучать язык.
  • 2
    Это действительно не должно быть опущено. Как не-Ruby парень, пытающийся выяснить это, этот ответ очень полезен!
Показать ещё 1 комментарий
17

Это просто метод, который определяет методы getter и setter для переменных экземпляра. Пример реализации:

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
  • 0
    обработка нескольких атрибутов таким способом - это здорово!
12

Я столкнулся с этой проблемой и написал несколько длинный ответ на этот вопрос. На это уже есть отличные ответы, но кто ищет больше разъяснений, я надеюсь, что мой ответ поможет

Инициализировать метод

Инициализация позволяет вам устанавливать данные в экземпляр объекта при создании экземпляра, а не устанавливать их в отдельной строке в вашем коде каждый раз, когда вы создаете новый экземпляр класса.

class Person
  attr_accessor :name

  def initialize(name)
    @name = name
  end


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

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

В приведенном выше коде мы устанавливаем имя "Denis", используя метод initialize, передавая Dennis через параметр в Initialize. Если бы мы хотели установить имя без метода initialize, мы могли бы сделать это следующим образом:

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

В приведенном выше коде мы устанавливаем имя, вызывая метод attr_accessor setter, используя person.name, вместо того, чтобы устанавливать значения при инициализации объекта.

Оба "метода" выполнения этой работы, но инициализация экономит время и строки кода.

Это единственное задание инициализации. Вы не можете вызвать инициализацию в качестве метода. Чтобы получить значения объекта экземпляра, вам нужно использовать геттеры и сеттеры (attr_reader (get), attr_writer (set) и attr_accessor (оба)). Подробнее см. Ниже.

Getters, Setters (attr_reader, attr_writer, attr_accessor)

Getters, attr_reader: вся цель getter - вернуть значение конкретной переменной экземпляра. Посетите приведенный ниже пример кода для его разбивки.

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

В приведенном выше коде вы вызываете методы "item_name" и "количество" в экземпляре Item "example". "Puts example.item_name" и "example.quantity" возвращают (или "получают" ) значение для параметров, которые были переданы в "пример", и отображают их на экране.

К счастью, в Ruby существует встроенный метод, который позволяет нам писать этот код более лаконично; метод attr_reader. См. Код ниже;

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

Этот синтаксис работает точно так же, только он сохраняет нам шесть строк кода. Представьте, если у вас было еще 5 состояний, относящихся к классу Item? Код быстро затянется.

Setters, attr_writer: то, что сначала меня пересекало с помощью методов setter, заключается в том, что в моих глазах он, похоже, выполняет идентичную функцию для метода инициализации. Ниже я объясняю разницу, основанную на моем понимании;

Как указано выше, метод initialize позволяет вам устанавливать значения для экземпляра объекта при создании объекта.

Но что, если вы хотите установить значения позже, после создания экземпляра или изменить их после их инициализации? Это будет сценарий, в котором вы бы использовали метод setter. ЧТО ТАКОЕ РАЗЛИЧИЯ. Вам не нужно "устанавливать" определенное состояние, когда вы сначала используете метод attr_writer.

Ниже приведен пример использования метода setter для объявления значения item_name для этого экземпляра класса Item. Обратите внимание, что мы продолжаем использовать getter-метод attr_reader, чтобы мы могли получить значения и распечатать их на экране, на всякий случай, если вы захотите проверить код самостоятельно.

class Item

attr_reader :item_name

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

end

Ниже приведен пример использования attr_writer, чтобы еще раз сократить наш код и сэкономить время.

class Item

attr_reader :item_name
attr_writer :item_name

end

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

Ниже приведен код повторения примера инициализации выше, где мы используем initialize для установки значения объектов item_name при создании.

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: выполняет функции как attr_reader, так и attr_writer, сохраняя еще одну строку кода.

11

Если вы знакомы с концепцией ООП, вы должны ознакомиться с методом getter и setter. attr_accessor делает то же самое в Ruby.

Геттер и сеттер на общем пути

class Person
  def name
    @name
  end

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

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

Метод сеттера

def name=(val)
  @name = val
end

Метод Getter

def name
  @name
end

Метод Getter и Setter в Ruby

class Person
  attr_accessor :name
end

person = Person.new
person.name = "Eshaan"
person.name # => "Eshaan"
  • 2
    идеальное объяснение! Это очень удобное поведение и может быть слишком легко отменено.
10

Простое объяснение без какого-либо кода

В большинстве приведенных ответов используется код. Это объяснение пытается ответить на него без использования каких-либо аналогий/истории:

Внешние стороны не могут получить доступ к секретам ЦРУ

  • Представьте себе действительно секретное место: ЦРУ. Никто не знает, что происходит в ЦРУ, кроме людей внутри ЦРУ. Другими словами, внешние лица не могут получить доступ к какой-либо информации в ЦРУ. Но поскольку нет ничего хорошего в организации, которая полностью секретна, определенная информация предоставляется внешнему миру - только то, что ЦРУ хочет, чтобы все знали о нем, например: например, директор ЦРУ, насколько экологичен этот отдел для всех других правительственных ведомств и т.д. Другая информация: например, кто является ее тайными оперативниками в Ираке или Афганистане, эти типы вещей, вероятно, останутся тайной в течение следующих 150 лет.

  • Если вы находитесь за пределами ЦРУ, вы можете получить доступ к информации, доступной только для общественности. Или использовать язык ЦРУ, вы можете получить доступ только к информации, которая "очищена".

  • Информация, которую ЦРУ хочет предоставить широкой публике вне ЦРУ, называется: атрибутами.

Значение атрибутов чтения и записи:

  • В случае ЦРУ большинство атрибутов являются "только для чтения". Это означает, что если вы являетесь сторонником ЦРУ, вы можете спросить: "Кто является директором ЦРУ?" и вы получите прямой ответ. Но то, что вы не можете сделать с атрибутами "только для чтения", - это внести изменения в ЦРУ. например, вы не можете позвонить и вдруг решите, что вы хотите, чтобы Ким Кардашян был директором, или что вы хотите, чтобы Пэрис Хилтон был главнокомандующим.

  • Если атрибуты дали вам доступ "запись", вы можете внести изменения, если хотите, даже если вы были снаружи. В противном случае, вы можете прочитать только одно.

    Другими словами, аксессоры позволяют вам делать запросы или вносить изменения в организации, которые в противном случае не разрешают внешним пользователям, в зависимости от того, читают ли аксессоры или ищут записи.

Объекты внутри класса могут легко обращаться друг к другу

  • С другой стороны, если вы уже были в ЦРУ, вы можете легко вызвать своего оперативника ЦРУ в Кабуле, потому что эта информация легко доступна, если вы уже находитесь внутри. Но если вы находитесь за пределами ЦРУ, вам просто не будет предоставлен доступ: вы не сможете узнать, кто они (доступ для чтения), и вы не сможете изменить свою миссию (доступ на запись).

Точная вещь с классами и возможность доступа к переменным, свойствам и методам внутри них. НТН! Любые вопросы, пожалуйста, спросите, и я надеюсь, что смогу уточнить.

  • 0
    Ваше объяснение имеет смысл! +1 Извините, вы уверены, что выражение «информация, которую очищает ЦРУ, верно?
  • 0
    в ЦРУ существуют различные уровни «разрешения»: например, «Совершенно секретно» (никто, кроме «Преза») или общественное доверие (каждый может прочитать эту информацию). ЦРУ на самом деле предоставляет много очень интересных фактов!
Показать ещё 1 комментарий
10

Я думаю, что часть того, что смущает новых рубистов/программистов (например, я), это:

"Почему я не могу просто указать экземпляру, что у него есть какой-либо атрибут (например, имя), и дать этому атрибуту значение всего одним махом?"

Немного более обобщен, но вот как он щелкнул меня:

Дано:

class Person
end

Мы не определили Person как нечто, что может иметь имя или любые другие атрибуты.

Итак, если мы тогда:

baby = Person.new

... и попробуйте дать им имя...

baby.name = "Ruth"

Мы получаем ошибку, потому что в Rubyland класс объекта Person не является тем, что связано или может иметь "имя"... еще!

НО мы можем использовать любой из этих методов (см. предыдущие ответы) как способ сказать: "У экземпляра класса Person (baby) теперь может быть атрибут, называемый" имя ", поэтому мы не только имеем синтаксический способ получения и настройки этого имени, но для нас это имеет смысл".

Опять же, ударив этот вопрос с немного другого и более общего угла, но я надеюсь, что это поможет следующему экземпляру класса Person, который находит свой путь к этой теме.

7

Проще говоря, он определит сеттер и getter для класса.

Обратите внимание, что

attr_reader :v is equivalant to 
def v
  @v
end

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

So

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

эквивалентны для определения сеттера и геттера для класса.

5

Просто attr-accessor создает методы getter и setter для указанных атрибутов

4

Другой способ понять это - выяснить, какой код ошибки он устраняет, имея attr_accessor.

Пример:

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

Доступны следующие методы:

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

Следующие методы вызывают ошибку:

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

owner и balance технически не являются методом, а атрибутом. Класс BankAccount не имеет def owner и def balance. Если да, то вы можете использовать две команды ниже. Но этих двух методов нет. Однако вы можете получить доступ к атрибутам, как если бы вы получили доступ к методу через attr_accessor!! Следовательно, слово attr_accessor. Атрибут. Accessor. Он обращается к таким атрибутам, как к доступу к методу.

Добавление attr_accessor :balance, :owner позволяет вам читать и писать balance и owner "метод". Теперь вы можете использовать последние 2 метода.

$ bankie.balance
$ bankie.owner
2

Определяет именованный атрибут для этого модуля, где имя является символом .id2name, создавая переменную экземпляра (@name) и соответствующий метод доступа для ее чтения. Также создается метод под названием name= для установки атрибута.

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

Чтобы обобщить атрибут accessor aka attr_accessor, вы получите два бесплатных метода.

Как и в Java, они называются getters и seters.

Многие ответы показали хорошие примеры, поэтому я просто буду кратким.

#the_attribute

и

#the_attribute =

В старых рубиновых документах хэш-тег # означает метод. Он также может включать префикс имени класса... MyClass # my_method

0

Я новичок в ruby и должен был просто разобраться в следующей странности. Может помочь кому-то еще в будущем. В конце концов, как было упомянуто выше, 2 функции (def myvar, def myvar =) обе неявно получают доступ к @myvar, но эти методы могут быть переопределены локальными объявлениями.

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" # - This is not running "myvar =", but instead calls setter for @myvar
    puts @myvar # E
    puts myvar # C
  end
end
0

Ниже приведен пример BankAccount. В attr_accessor определены имя и адрес, поэтому они передаются объекту при создании объекта. Также будет использоваться для выполнения операций чтения и записи. Для attr_reader атрибуты передаются объекту во время инициализации объекта класса.


    class Account

      attr_accessor :name, :address
      attr_reader :id, :balance 

      def initialize(name, address)
        self.name = name
        self.address = address
        @balance = 0
        @id = Random.rand(100)
      end

      def deposit(money)
        @balance += money 
        puts "Deposited Rs #{money}" 
      end

      def check_balance
        puts "Available balance is Rs #{balance}" 
      end
    end

    account1 = Account.new('John','Singapore')
    account1.deposit(100)
    account1.check_balance

0

Атрибуты и методы доступа

Атрибуты - это компоненты класса, к которым можно получить доступ извне объекта. Они известны как свойства во многих других языках программирования. Их значения доступны с использованием "точечной нотации", как в object_name.attribute_name. В отличие от Python и нескольких других языков, Ruby не разрешает доступ к экземплярам непосредственно извне объекта.

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>

В приведенном выше примере c является экземпляром (объектом) класса Car. Мы безуспешно пытались прочитать значение переменной экземпляра колес вне объекта. Случилось так, что Ruby попытался вызвать метод с именем wheel внутри объекта c, но такой метод не был определен. Короче говоря, object_name.attribute_name пытается вызвать метод с именем attribute_name внутри объекта. Чтобы получить доступ к значению переменной колеса снаружи, нам нужно реализовать метод экземпляра с помощью этого имени, которое будет возвращать значение этой переменной при вызове. Это называется методом доступа. В общем контексте программирования обычным способом доступа к переменной экземпляра извне объекта является реализация методов доступа, также известных как методы getter и setter. Геттер позволяет считывать значение переменной, определенной внутри класса, снаружи, а сеттер позволяет записывать ее извне.

В следующем примере мы добавили методы getter и setter в класс Car для доступа к переменной колес извне объекта. Это не "рубиновый путь" определения геттеров и сеттеров; он служит только для иллюстрации того, какие методы getter и 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

Приведенный выше пример работает, и аналогичный код обычно используется для создания методов getter и setter на других языках. Однако Ruby предоставляет более простой способ сделать это: три встроенных метода, называемых attr_reader, attr_writer и attr_acessor. Метод attr_reader делает переменную экземпляра, читаемую снаружи, attr_writer делает ее доступной для записи, а attr_acessor делает ее доступной для чтения и записи.

Приведенный выше пример можно переписать следующим образом.

class Car
  attr_accessor :wheels
end

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

В приведенном выше примере атрибут колес будет доступен для чтения и записи извне объекта. Если вместо attr_accessor мы использовали attr_reader, он был бы доступен только для чтения. Если бы мы использовали attr_writer, это было бы только для записи. Эти три метода не являются геттерами и сеттерами сами по себе, но, когда они называются, они создают методы получения и настройки для нас. Это методы, которые динамически (программно) генерируют другие методы; который называется метапрограммированием.

Первый (более длинный) пример, который не использует встроенные методы Ruby, должен использоваться только тогда, когда требуется дополнительный код в методах getter и setter. Например, для метода установки может потребоваться проверка данных или выполнение некоторых вычислений до назначения значения переменной экземпляра.

Можно получить доступ (чтение и запись) переменных экземпляра вне объекта, используя встроенные методы instance_variable_get и instance_variable_set. Однако это редко оправдано и обычно плохой идеей, поскольку обход инкапсуляции имеет тенденцию наносить всевозможные хаосы.

-1

Хммм. Много хороших ответов. Вот мои небольшие центы.

  • attr_accessor - это простой метод, который помогает нам в очистке (DRY-ing) до повторяющихся методов getter and setter.

  • Итак, мы можем больше сосредоточиться на написании бизнес-логики и не беспокоиться о сеттерах и геттерах.

-2

Основные функции attr_accessor по сравнению с другими - это возможность доступа к данным из других файлов.
Таким образом, вы обычно будете иметь attr_reader или attr_writer, но хорошей новостью является то, что Ruby позволяет объединить эти два вместе с attr_accessor. Я считаю, что это мой метод, потому что он более округлый или универсальный. Кроме того, обратите внимание, что в Rails это устраняется, потому что это делает это для вас в задней части. Итак, другими словами: вам лучше использовать attr_acessor над двумя другими, потому что вам не нужно беспокоиться о том, чтобы быть конкретным, аксессуар покрывает все это. Я знаю, что это скорее общее объяснение, но оно помогло мне в качестве новичка.

Надеюсь, это помогло!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню