Что такое attr_accessor в Ruby?
Мне трудно понять attr_accessor
в рубине. Может кто-то объяснить это мне?
20 ответов
Допустим, у вас есть класс 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
Даже это может стать повторяющимся. Когда вы хотите, чтобы читатель и писатель просто использовали аксессор!
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
методы на самом деле генерируют методы для вас, читайте другие ответы, книги, рубиновые документы.
attr_accessor - это просто метод. (Ссылка должна дать более глубокое представление о том, как она работает - посмотрите на сгенерированные пары методов, и учебник должен показать вам, как ее использовать.)
Хитрость в том, что class
это не определение в Ruby (это "просто определение" в таких языках, как C++ и Java), но это выражение, которое оценивает. Именно во время этой оценки, когда attr_accessor
вызывается метод, который в свою очередь изменяет текущий класс - запомните неявный получатель: self.attr_accessor
, где self
является "открытым" объектом класса в этой точке.
Нужда в attr_accessor
а друзья, есть, хорошо
Ruby, как и Smalltalk, не разрешает доступ к переменным экземпляра вне методов1 для этого объекта. То есть переменные экземпляра не могут быть доступны в
x.y
форма, как это принято в, скажем, Java или даже Python. В рубинеy
всегда принимается как сообщение для отправки (или "метод для вызова"). Таким образомattr_*
методы создают обертки, которые прокси@variable
доступ через динамически созданные методы.Котел отстой
Надеюсь, что это проясняет некоторые мелочи. Удачного кодирования.
1 Это не совсем верно, и в этом есть некоторые "методы", но нет синтаксической поддержки доступа к "общедоступным переменным экземпляра".
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
attr_accessor
очень просто:
attr_accessor :foo
это ярлык для:
def foo=(val)
@foo = val
end
def foo
@foo
end
это не что иное, как получатель / установщик объекта
По сути, они подделывают общедоступные атрибуты данных, которых нет в Ruby.
Это просто метод, который определяет методы получения и установки для переменных экземпляра. Примером реализации будет:
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
Если вы знакомы с концепцией ООП, вы должны быть знакомы с методами получения и установки. 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
Метод получения
def name
@name
end
Метод получения и установки в Ruby
class Person
attr_accessor :name
end
person = Person.new
person.name = "Eshaan"
person.name # => "Eshaan"
Я тоже столкнулся с этой проблемой и написал довольно длинный ответ на этот вопрос. На этот счет уже есть несколько отличных ответов, но всем, кто ищет уточнения, я надеюсь, что мой ответ поможет
Метод инициализации
Initialize позволяет вам устанавливать данные для экземпляра объекта при создании экземпляра, а не устанавливать их в отдельной строке кода каждый раз, когда вы создаете новый экземпляр класса.
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. Если бы мы хотели установить имя без метода инициализации, мы могли бы сделать это так:
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 с использованием person.name, а не устанавливая значения при инициализации объекта.
Оба "метода" выполнения этой работы, но инициализация экономят нам время и строки кода.
Это единственная работа по инициализации. Вы не можете вызвать инициализацию как метод. Чтобы фактически получить значения объекта экземпляра, вам нужно использовать методы получения и установки (attr_reader (get), attr_writer(set) и attr_accessor(оба)). Смотрите ниже для более подробной информации о них.
Получатели, Установщики (attr_reader, attr_writer, attr_accessor)
Getters, attr_reader: Цель геттера - вернуть значение определенной переменной экземпляра. Посетите пример кода ниже для разбивки этого.
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" и "amount" в экземпляре Item "example". "Put example.item_name" и "example.quantity" вернут (или "получат") значение для параметров, переданных в "example", и отобразят их на экране.
К счастью, в 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? Код станет длинным быстро.
Сеттеры, attr_writer: Сначала меня поразили методы сеттера: мне показалось, что он выполняет ту же функцию, что и метод инициализации. Ниже я объясняю разницу на основе моего понимания;
Как указано выше, метод initialize позволяет вам устанавливать значения для экземпляра объекта при создании объекта.
Но что, если вы захотите установить значения позже, после создания экземпляра или изменить их после инициализации? Это будет сценарий, в котором вы будете использовать метод установки. ЭТО РАЗНИЦА. Вам не нужно "устанавливать" определенное состояние, когда вы изначально используете метод attr_writer.
Приведенный ниже код является примером использования метода установки для объявления значения 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"
Приведенный ниже код является повторением приведенного выше примера инициализации, где мы используем инициализацию для установки значения объектов 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, сохраняя вам еще одну строку кода.
Большинство из приведенных выше ответов используют код. Это объяснение пытается ответить на него без использования какого-либо кода:
Объяснение по аналогии
Внешние стороны не могут получить доступ к внутренним секретам ЦРУ
Давайте представим себе действительно секретное место: ЦРУ. Никто не знает, что происходит в ЦРУ, кроме людей внутри ЦРУ. Другими словами, внешние люди не могут получить доступ к какой-либо информации в ЦРУ. Но поскольку организация, которая является полностью секретной, бесполезна, определенная информация становится доступной для внешнего мира - только то, о чем ЦРУ хочет, чтобы все знали, конечно: например, директор ЦРУ, насколько экологически безопасен этот отдел ко всем другим правительственным ведомствам и т. д. Другая информация: например, кто является его тайными оперативниками в Ираке или Афганистане - такие вещи, вероятно, останутся секретом в течение следующих 150 лет.
Если вы находитесь за пределами ЦРУ, вы можете получить доступ только к той информации, которую он предоставил общественности. Или использовать язык ЦРУ, вы можете получить доступ только к информации, которая "очищена".
Информация, которую ЦРУ хочет сделать доступной для широкой публики за пределами ЦРУ, называется: атрибуты.
Значение атрибутов read и write:
В случае ЦРУ большинство атрибутов "только для чтения". Это означает, что если вы сторонник ЦРУ, вы можете спросить: "Кто является директором ЦРУ?" и вы получите прямой ответ. Но что вы не можете делать с атрибутами "только для чтения", так это вносить изменения в CIA. например, вы не можете позвонить и вдруг решите, что хотите, чтобы Ким Кардашьян был директором, или что вы хотите, чтобы Пэрис Хилтон была главнокомандующим.
Если атрибуты предоставили вам доступ для "записи", то вы можете вносить изменения, если хотите, даже если вы были снаружи. В противном случае вы можете только читать.
Другими словами, средства доступа позволяют вам делать запросы или вносить изменения в организации, которые в противном случае не допускают внешних сотрудников, в зависимости от того, являются ли средства доступа доступными для чтения или записи.
Объекты внутри класса могут легко получить доступ друг к другу
- С другой стороны, если вы уже были в ЦРУ, вы могли бы легко позвонить своему оперативнику ЦРУ в Кабуле и спросить его, не хочет ли он выпить пива с местным кабульским информатором после работы. Но если вы находитесь за пределами ЦРУ, вам просто не будет предоставлен доступ: вы не сможете узнать, кто они (доступ для чтения), и вы не сможете изменить их миссию (доступ для записи).
Точно то же самое с классами и вашей возможностью доступа к переменным, свойствам и методам внутри них. НТН! Любые вопросы, пожалуйста, задавайте, и я надеюсь, что смогу уточнить.
Я думаю, что часть того, что смущает новых Rubyists/ программистов (таких как я):
"Почему я не могу просто сказать экземпляру, что у него есть какой-либо заданный атрибут (например, имя), и дать этому атрибуту значение всего одним махом?"
Немного более обобщенно, но вот как это у меня получилось:
Дано:
class Person
end
Мы не определили Person как нечто, что может иметь имя или любые другие атрибуты по этому вопросу.
Так что если мы тогда:
baby = Person.new
... и попытаться дать им имя...
baby.name = "Ruth"
Мы получаем ошибку, потому что в Rubyland класс объекта Person не является чем-то, что связано или способно иметь "имя"... пока!
НО мы можем использовать любой из данных методов (см. Предыдущие ответы) как способ сказать: "Экземпляр класса Person (baby
) теперь может иметь атрибут "имя", поэтому у нас есть не только синтаксический способ получения и установки этого имени, но и для нас это имеет смысл ".
Опять же, задание этого вопроса с несколько иной и более общей точки зрения, но я надеюсь, что это поможет следующему экземпляру класса Person, который найдет свой путь к этой теме.
Проще говоря, это определит сеттер и геттер для класса.
Обратите внимание, что
attr_reader :v is equivalant to
def v
@v
end
attr_writer :v is equivalant to
def v=(value)
@v=value
end
Так
attr_accessor :v which means
attr_reader :v; attr_writer :v
эквивалентны, чтобы определить сеттер и геттер для класса.
Другой способ понять это - выяснить, какой код ошибки он устраняет, имея 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
Просто attr-accessor
создает getter
а также setter
методы для указанных атрибутов
Несмотря на большое количество существующих ответов, мне кажется, ни один из них не объясняет реальный механизм, задействованный здесь. Это метапрограммирование; он использует следующие два факта:
Вы можете модифицировать модуль/класс на лету
Объявление модуля/класса само по себе является исполняемым кодом
Итак, представьте себе следующее:
class Nameable
def self.named(whatvalue)
define_method :name do whatvalue end
end
end
Мы объявляем метод класса, который при вызове со значением создает вызываемый метод экземпляра, который возвращает это значение. Это часть метапрограммирования.
Теперь мы создадим подкласс этого класса:
class Dog < Nameable
named "Fido"
end
Что мы только что сделали? Ну, в объявлении класса исполняемый код выполняется со ссылкой на класс. Так что голое слово на самом деле является вызовом метода класса, который мы унаследовали от Nameable; и мы передаем строку
И что делает метод класса? Он создает метод экземпляра с именем
def name
"Fido"
end
Не верите мне? Затем посмотрите на это маленькое движение:
puts Dog.new.name #=> Fido
Зачем я тебе все это рассказал? Потому что то, что я только что сделал с
Определяет именованный атрибут для этого модуля, где имя - это symbol.id2name, создавая переменную экземпляра (@name) и соответствующий метод доступа для его чтения. Также создает метод с именем name= для установки атрибута.
module Mod
attr_accessor(:one, :two)
end
Mod.instance_methods.sort #=> [:one, :one=, :two, :two=]
Подводя итог, атрибут accessor aka attr_accessor дает вам два бесплатных метода.
Как и в Java, их называют геттерами и сеттерами.
Многие ответы показали хорошие примеры, поэтому я буду кратким.
#the_attribute
а также
# The_attribute=
В старых документах ruby хэш-тег # означает метод. Он также может включать префикс имени класса... MyClass#my_method
Я новичок в ruby и должен был просто разобраться со следующей странностью. Может помочь кому-то еще в будущем. В конце концов, как было упомянуто выше, обе функции (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" # not running "myvar =", but instead calls setter for @myvar
puts @myvar # E
puts myvar # C
end
end
Атрибуты и методы доступа
Атрибуты являются компонентами класса, к которым можно получить доступ снаружи объекта. Они известны как свойства во многих других языках программирования. Их значения доступны с помощью "точечной нотации", как в 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, но такой метод не был определен. Короче говоря, имя_объекта.attribute_name пытается вызвать метод с именем attribute_name внутри объекта. Чтобы получить доступ к значению переменной wheel извне, нам нужно реализовать метод экземпляра с таким именем, который будет возвращать значение этой переменной при вызове. Это называется методом доступа. В общем контексте программирования обычным способом доступа к переменной экземпляра извне объекта является реализация методов доступа, также известных как методы получения и установки. Метод получения позволяет читать значение переменной, определенной в классе, извне, а метод установки позволяет записывать ее извне.
В следующем примере мы добавили методы getter и setter в класс Car, чтобы получить доступ к переменной wheel снаружи объекта. Это не "Рубиновый способ" определения методов получения и установки; он служит только для иллюстрации того, что делают методы 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
Приведенный выше пример работает, и аналогичный код обычно используется для создания методов получения и установки на других языках. Тем не менее, 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
В приведенном выше примере атрибут wheel будет доступен для чтения и записи извне объекта. Если бы вместо attr_accessor мы использовали attr_reader, он был бы доступен только для чтения. Если бы мы использовали attr_writer, он был бы только для записи. Эти три метода сами по себе не являются методами получения и установки, но при вызове они создают методы получения и установки для нас. Это методы, которые динамически (программно) генерируют другие методы; это называется метапрограммирование.
Первый (более длинный) пример, в котором не используются встроенные методы Ruby, следует использовать только тогда, когда в методах get и set требуется дополнительный код. Например, методу установки может потребоваться проверить данные или выполнить некоторые вычисления, прежде чем присваивать значение переменной экземпляра.
Доступ к переменным экземпляра (чтение и запись) возможен извне объекта с помощью встроенных методов instance_variable_get и instance_variable_set. Тем не менее, это редко оправдано и, как правило, является плохой идеей, так как обход инкапсуляции имеет тенденцию вызывать всевозможные разрушения.
Хммм. Много хороших ответов. Вот мои несколько центов на это.
attr_accessor
это простой метод, который помогает нам в очистке (СУХОЙ) до повторяющихсяgetter and setter
методы.Так что мы можем больше сосредоточиться на написании бизнес-логики и не беспокоиться о методах установки и получения.
Основная функция attr_accessor по сравнению с другими - это возможность доступа к данным из других файлов.
Так что обычно у вас есть attr_reader или attr_writer, но хорошая новость в том, что Ruby позволяет вам объединить эти два вместе с attr_accessor. Я думаю, что это мой метод go, потому что он более округлый или универсальный. Кроме того, имейте в виду, что в Rails это устранено, потому что оно делает это за вас в бэкэнде. Другими словами: вам лучше использовать attr_acessor поверх двух других, потому что вам не нужно беспокоиться о том, чтобы быть конкретным, аксессор покрывает все это. Я знаю, что это скорее общее объяснение, но оно помогло мне как новичку.
Надеюсь, это помогло!