что такое attr\_accessor в ruby?

<p>мне трудно понять <code>attr\_accessor</code> в ruby. может кто-то объяснить это мне?</p>

<p>допустим, у вас есть класс <code>person</code>.</p><pre><code>class person end person = person.new person.name # => no method error </code></pre><p>очевидно, мы никогда не определяли метод <code>name</code>. давайте сделаем это.</p><pre><code>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 </code></pre><p>ага, мы можем прочитать имя, но это не значит, что мы можем присвоить имя. это два разных метода. первый называется<em> reader </em>, а второй называется<em> writer </em>. мы еще не создали писателя, поэтому давайте сделаем это.</p><pre><code>class person def name @name end def name=(str) @name = str end end person = person.new person.name = 'dennis' person.name # => "dennis" </code></pre><p>потрясающие. теперь мы можем писать и читать переменную экземпляра <code>@name</code>, используя методы чтения и записи. за исключением того, что это делается так часто, зачем тратить время на написание этих методов каждый раз? мы можем сделать это проще.</p><pre><code>class person attr\_reader :name attr\_writer :name end </code></pre><p>даже это может стать повторяющимся. когда вы хотите, чтобы читатель и писатель просто использовали аксессор!</p><pre><code>class person attr\_accessor :name end person = person.new person.name = "dennis" person.name # => "dennis" </code></pre><p>работает так же! и угадайте, что: переменная экземпляра <code>@name</code> в нашем объекте person будет установлена точно так же, как когда мы делали это вручную, так что вы можете использовать ее в других методах.</p><pre><code>class person attr\_accessor :name def greeting "hello #{@name}" end end person = person.new person.name = "dennis" person.greeting # => "hello dennis" </code></pre><p>вот и все. чтобы понять, как методы <code>attr\_reader</code>, <code>attr\_writer</code> и <code>attr\_accessor</code> фактически генерируют для вас методы, прочитайте другие ответы, книги, документы по ruby. </p>

<p> <a href="http://www.ruby-doc.org/core-2.0.0/module.html#method-i-attr\_accessor">attr\_accessor</a> - это <em>просто метод</em>. (ссылка должна дать более полное представление о том, как она работает - посмотрите на сгенерированные пары методов, и учебник должен показать вам, как ее использовать.)</p><p>хитрость в том, что <code>class</code> - это <em>не определение</em> в ruby (это «просто определение» в таких языках, как c++ и java), но это выражение <em>, которое оценивает</em>. именно во время этой оценки вызывается метод <code>attr\_accessor</code>, который, в свою очередь, изменяет текущий класс - запомните неявный получатель: <code>self.attr\_accessor</code>, где <code>self</code> - это объект класса «open» в этой точке.</p><p>потребность в <code>attr\_accessor</code> и друзьях, ну, в общем:</p><ol> <li><p>ruby, как и smalltalk, не позволяет обращаться к переменным экземпляра вне методов<sup>1</sup> для этого объекта. то есть переменные экземпляра не могут быть доступны в форме <code>x.y</code>, как это принято в скажем, java или даже python. в ruby <code>y</code> всегда принимается как сообщение для отправки (или «метод для вызова»). таким образом, методы <code>attr\_*</code> создают оболочки, которые обеспечивают доступ к экземпляру <code>@variable</code> через динамически создаваемые методы.</p></li> <li><p>котел отстой</p></li> </ol><p>надеюсь, что это проясняет некоторые мелочи. удачного кодирования.</p><hr><p><sup>1</sup> это не совсем верно, и есть <a href="https://stackoverflow.com/questions/1074729/get-the-value-of-an-instance-variable-given-its-name">некоторые «методы» вокруг этого</a> , но нет синтаксической поддержки доступа к «общедоступной переменной экземпляра».</p>

<p><code>attr\_accessor</code> (как указано @pst) просто метод. что он делает, так это создает больше методов для вас.</p><p>итак, этот код здесь:</p><pre><code>class foo attr\_accessor :bar end </code></pre><p>эквивалентно этому коду:</p><pre><code>class foo def bar @bar end def bar=( new\_value ) @bar = new\_value end end </code></pre><p>вы можете написать этот метод самостоятельно в ruby:</p><pre><code>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 </code></pre>

<p><code>attr\_accessor</code> очень прост:</p><pre><code>attr\_accessor :foo </code></pre><p>это ярлык для:</p><pre><code>def foo=(val) @foo = val end def foo @foo end </code></pre><p>это не что иное, как метод получения/установки объекта</p>

<p>по сути, они подделывают общедоступные атрибуты данных, которых нет в ruby.</p>

<p>это просто метод, который определяет методы получения и установки для переменных экземпляра. примером реализации будет:</p><pre><code>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 </code></pre>

<p>я тоже столкнулся с этой проблемой и написал довольно длинный ответ на этот вопрос. на этот счет уже есть несколько отличных ответов, но всем, кто ищет уточнения, я надеюсь, что мой ответ поможет</p><p>метод инициализации</p><p>initialize позволяет вам устанавливать данные для экземпляра объекта при создании экземпляра, а не устанавливать их в отдельной строке кода каждый раз, когда вы создаете новый экземпляр класса. </p><pre><code>class person attr\_accessor :name def initialize(name) @name = name end def greeting "hello #{@name}" end end person = person.new("denis") puts person.greeting </code></pre><p>в приведенном выше коде мы устанавливаем имя «denis» с помощью метода initialize, передавая dennis через параметр в initialize. если бы мы хотели установить имя без метода инициализации, мы могли бы сделать это так:</p><pre><code>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 </code></pre><p>в приведенном выше коде мы устанавливаем имя, вызывая метод установщика attr\_accessor с использованием person.name, а не устанавливая значения при инициализации объекта. </p><p>оба «метода» выполнения этой работы, но инициализация экономят нам время и строки кода.</p><p>это единственная работа по инициализации. вы не можете вызвать инициализацию как метод. чтобы фактически получить значения объекта экземпляра, вам нужно использовать методы получения и установки (attr\_reader (get), attr\_writer (set) и attr\_accessor (оба)). смотрите ниже для более подробной информации о них.</p><p>получатели, установщики (attr\_reader, attr\_writer, attr\_accessor)</p><p>getters, attr\_reader: цель геттера - вернуть значение определенной переменной экземпляра. посетите пример кода ниже для разбивки этого. </p><pre><code>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 </code></pre><p>в приведенном выше коде вы вызываете методы «item\_name» и «amount» в экземпляре item «example». «put example.item\_name» и «example.quantity» вернут (или «получат») значение для параметров, переданных в «example», и отобразят их на экране.</p><p>к счастью, в ruby есть собственный метод, который позволяет нам писать этот код более кратко; метод attr\_reader. смотрите код ниже;</p><pre><code>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 </code></pre><p>этот синтаксис работает точно так же, только он экономит нам шесть строк кода. представьте, если бы у вас было еще 5 состояний, относящихся к классу item? код станет длинным быстро.</p><p>сеттеры, attr\_writer: сначала меня поразили методы сеттера: мне показалось, что он выполняет функцию, идентичную методу инициализации. ниже я объясняю разницу на основе моего понимания; </p><p>как указано выше, метод initialize позволяет вам устанавливать значения для экземпляра объекта при создании объекта. </p><p>но что, если вы хотите установить значения позже, после создания экземпляра или изменить их после их инициализации? это будет сценарий, в котором вы будете использовать метод установки. это is разница. вам не нужно «устанавливать» определенное состояние, когда вы изначально используете метод attr\_writer. </p><p>приведенный ниже код является примером использования метода установки для объявления значения item\_name для этого экземпляра класса item. обратите внимание, что мы продолжаем использовать метод getter attr\_reader, чтобы мы могли получать значения и выводить их на экран, на тот случай, если вы захотите протестировать код самостоятельно. </p><pre><code>class item attr\_reader :item\_name def item\_name=(str) @item\_name = (str) end end </code></pre><p>приведенный ниже код является примером использования attr\_writer, чтобы еще раз сократить наш код и сэкономить нам время.</p><pre><code>class item attr\_reader :item\_name attr\_writer :item\_name end item = item.new puts item.item\_name = "tv" </code></pre><p>приведенный ниже код является повторением приведенного выше примера инициализации, где мы используем инициализацию для установки значения объектов item\_name при создании.</p><pre><code>class item attr\_reader :item\_name def initialize(item\_name) @item\_name = item\_name end end item = item.new("tv") puts item.item\_name </code></pre><p>attr\_accessor: выполняет функции как attr\_reader, так и attr\_writer, сохраняя вам еще одну строку кода.</p>

<p>если вы знакомы с концепцией oop, вы должны быть знакомы с методами получения и установки .. attr\_accessor делает то же самое в ruby.</p><p><strong> getter и setter в общем пути </strong></p><pre><code>class person def name @name end def name=(str) @name = str end end person = person.new person.name = 'eshaan' person.name # => "eshaan" </code></pre><p><strong> метод сеттера </strong></p><pre><code>def name=(val) @name = val end </code></pre><p><strong> метод геттера </strong> </p><pre><code>def name @name end </code></pre><p><strong> метод получения и установки в ruby </strong></p><pre><code>class person attr\_accessor :name end person = person.new person.name = "eshaan" person.name # => "eshaan" </code></pre>

<h3>простое объяснение без кода</h3><p>в большинстве приведенных выше ответов используется код. это объяснение пытается ответить на него, не используя ничего, по аналогии/истории:</p><p><strong> внешние стороны не могут получить доступ к внутренним секретам цру </strong></p><ul> <li><p>давайте представим себе действительно секретное место: цру. никто не знает, что происходит в цру, кроме людей внутри цру. другими словами, внешние люди не могут получить доступ к какой-либо информации в цру. но поскольку нехорошо иметь организацию, которая является полностью секретной, определенная информация становится доступной для внешнего мира - только то, о чем цру хочет, чтобы все знали, конечно: например, директор цру, насколько экологически безопасен этот департамент по сравнению со всеми другими правительственными департаментами и т. д. другая информация: например, кто является его тайным агентом в ираке или афганистане - такие вещи, вероятно, останутся секретом в течение следующих 150 лет.</p></li> <li><p>если вы находитесь за пределами цру, вы можете получить доступ только к той информации, которую он предоставил общественности. или на языке цру вы можете получить доступ только к информации, которая «очищена».</p></li> <li><p>информация, которую цру хочет сделать доступной для широкой публики за пределами цру, называется:<strong> атрибуты. </strong> </p></li> </ul><p><strong> значение атрибутов чтения и записи: </strong></p><ul> <li><p>в случае цру, большинство атрибутов «только для чтения». это означает, что если вы являетесь стороной <em>external</em> для цру, вы можете<strong> спросить: </strong>"кто является директором цру?" и вы получите прямой ответ. но то, что вы<strong> не можете </strong>делать с атрибутами "только для чтения", - это вносить изменения в cia. например вы не можете позвонить и вдруг<strong> решить </strong>что вы хотите, чтобы ким кардашьян был директором, или что вы хотите, чтобы пэрис хилтон была главнокомандующим. </p></li> <li><p>если атрибуты предоставили вам доступ для «записи», то вы можете вносить изменения, если хотите, даже если вы были снаружи. в противном случае вы можете только читать.</p> <p>другими словами, средства доступа позволяют вам делать запросы или вносить изменения в организации, которые в противном случае не допускают внешних сотрудников, в зависимости от того, являются ли средства доступа доступными для чтения или записи.</p></li> </ul><p><strong> объекты внутри класса могут легко получить доступ друг к другу </strong></p><ul> <li>с другой стороны, если вы уже были <em>внутри</em> цру, вы могли бы легко вызвать своего сотрудника цру в кабуле, потому что эта информация легко доступна, если вы уже внутри. но если вы <em>находитесь за пределами</em> цру, вам просто не будет предоставлен доступ: вы не сможете узнать, кто они (доступ для чтения), и вы не сможете изменить свою миссию (доступ для записи). ,.</li> </ul><p>точно то же самое с классами и вашей возможностью доступа к переменным, свойствам и методам внутри них. нтн! любые вопросы, пожалуйста, задавайте, и я надеюсь, что смогу уточнить.</p>

<p>я думаю, что часть того, что смущает новых rubyists/программистов (таких как я):</p><p>«почему я не могу просто сказать экземпляру, что у него есть какой-либо заданный атрибут (например, имя), и присвоить этому атрибуту значение одним махом?»</p><p>немного более обобщенно, но вот как это у меня получилось:</p><p>дано:</p><pre><code>class person end </code></pre><p>мы не определили person <em>как нечто, что может иметь имя</em> или любые другие атрибуты по этому вопросу. </p><p>так что если мы тогда:</p><pre><code>baby = person.new </code></pre><p>... и попытаться дать им имя ...</p><pre><code>baby.name = "ruth" </code></pre><p>мы получаем<strong> error </strong>, потому что в rubyland класс объекта person не является чем-то, что связано или способно иметь «имя» ... пока!</p><p>но мы можем использовать любой из данных методов (см. предыдущие ответы) как способ сказать: «экземпляр класса person (<code>baby</code>) <em>может теперь</em> иметь атрибут с именем« name », поэтому мы не только имеем синтаксический способ получения и установки этого имени, но для нас это имеет смысл ».</p><p>опять же, задание этого вопроса с несколько иной и более общей точки зрения, но я надеюсь, что это поможет следующему экземпляру класса person, который найдет свой путь к этой теме.</p>

<p>проще говоря, это определит сеттер и геттер для класса.</p><p>обратите внимание, что </p><pre><code>attr\_reader :v is equivalant to def v @v end attr\_writer :v is equivalant to def v=(value) @v=value end </code></pre><p>так </p><pre><code>attr\_accessor :v which means attr\_reader :v; attr\_writer :v </code></pre><p>эквивалентны, чтобы определить сеттер и геттер для класса.</p>

<p>просто <code>attr-accessor</code> создает методы <code>getter</code> и <code>setter</code> для указанных атрибутов</p>

<p>другой способ понять это - выяснить, какой код ошибки он устраняет, используя <code>attr\_accessor</code>. </p><p>пример:</p><pre><code>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 </code></pre><p>доступны следующие методы:</p><pre><code>$ bankie = bankaccout.new("iggy") $ bankie $ bankie.deposit(100) $ bankie.withdraw(5) </code></pre><p>следующие методы выдают ошибку:</p><pre><code>$ bankie.owner #undefined method owner'... $ bankie.balance #undefined method `balance'... </code></pre><p><code>owner</code> и <code>balance</code>, технически, не <em>метод</em>, а атрибут. класс bankaccount не имеет <code>def owner</code> и <code>def balance</code>. если это так, то вы можете использовать две команды ниже. но этих двух методов нет. однако вы можете <em>получить доступ к атрибутам</em>, как если бы вы <em>получили доступ к</em> методу через <code>attr\_accessor</code> !! <em>отсюда и слово <code>attr\_accessor</code></em>. атрибут. accessor. он обращается к атрибутам, как если бы вы обращались к методу. </p><p>добавление <code>attr\_accessor :balance, :owner</code> позволяет вам читать и писать <code>balance</code> и <code>owner</code> «метод». теперь вы можете использовать последние 2 метода.</p><pre><code>$ bankie.balance $ bankie.owner </code></pre>

<p>определяет именованный атрибут для этого модуля, где имя - это symbol.id2name, создавая переменную экземпляра (@name) и соответствующий метод доступа для его чтения. также создает метод с именем name = для установки атрибута.</p><pre><code>module mod attr\_accessor(:one, :two) end mod.instance\_methods.sort #=> [:one, :one=, :two, :two=] </code></pre>

<p>подводя итог, атрибут accessor aka attr\_accessor дает вам два бесплатных метода.</p><p>как и в java, их называют геттерами и сеттерами.</p><p>многие ответы показали хорошие примеры, поэтому я буду кратким.</p><p>#the\_attribute</p><p>а также</p><p># the\_attribute =</p><p>в старых документах ruby хэш-тег # означает метод . он также может включать префикс имени класса ... myclass # my\_method</p>

<p>я новичок в ruby и должен был разобраться со следующей странностью. может помочь кому-то еще в будущем. в конце концов, как было упомянуто выше, 2 функции (def myvar, def myvar =) обе неявно получают доступ к @myvar, но эти методы могут быть переопределены локальными объявлениями.</p><pre><code>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 </code></pre>

<h3>атрибуты и методы доступа</h3><p>атрибуты являются компонентами класса, к которым можно получить доступ снаружи объекта. они известны как свойства во многих других языках программирования. их значения доступны с помощью «точечной нотации», как в object\_name.attribute\_name. в отличие от python и некоторых других языков, ruby не позволяет обращаться к переменным экземпляра напрямую из-за пределов объекта.</p><pre><code>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> </code></pre><p>в приведенном выше примере c является экземпляром (объектом) класса car. мы безуспешно пытались прочитать значение переменной экземпляра колеса снаружи объекта. случилось так, что ruby попытался вызвать метод с именем wheel внутри объекта c, но такой метод не был определен. короче говоря, object\_name.attribute\_name пытается вызвать метод с именем attribute\_name внутри объекта. чтобы получить доступ к значению переменной wheel извне, нам нужно реализовать метод экземпляра с таким именем, который будет возвращать значение этой переменной при вызове. это называется методом доступа. в общем контексте программирования обычным способом доступа к переменной экземпляра вне объекта является реализация методов доступа, также известных как методы получения и установки. метод получения позволяет читать значение переменной, определенной в классе, извне, а метод установки позволяет записывать его извне.</p><p>в следующем примере мы добавили методы getter и setter в класс car, чтобы получить доступ к переменной wheel снаружи объекта. это не «рубиновый способ» определения методов получения и установки; он служит только для иллюстрации того, что делают методы getter и setter.</p><pre><code>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 </code></pre><p>приведенный выше пример работает, и аналогичный код обычно используется для создания методов получения и установки на других языках. тем не менее, ruby предоставляет более простой способ сделать это: три встроенных метода, называемые attr\_reader, attr\_writer и attr\_acessor. метод attr\_reader делает переменную экземпляра доступной для чтения извне, attr\_writer делает ее доступной для записи, а attr\_acessor делает ее читаемой и доступной для записи. </p><p>приведенный выше пример можно переписать так.</p><pre><code>class car attr\_accessor :wheels end f = car.new f.wheels = 4 f.wheels # output: => 4 </code></pre><p>в приведенном выше примере атрибут wheel будет доступен для чтения и записи извне объекта. если бы вместо attr\_accessor мы использовали attr\_reader, он был бы доступен только для чтения. если бы мы использовали attr\_writer, он был бы только для записи. эти три метода сами по себе не являются методами получения и установки, но при вызове они создают методы получения и установки для нас. это методы, которые динамически (программно) генерируют другие методы; это называется метапрограммирование.</p><p>первый (более длинный) пример, в котором не используются встроенные методы ruby, следует использовать только тогда, когда в методах get и set требуется дополнительный код. например, методу установки может потребоваться проверить данные или выполнить некоторые вычисления, прежде чем присваивать значение переменной экземпляра. </p><p>доступ к переменным экземпляра (чтение и запись) возможен извне объекта с помощью встроенных методов instance\_variable\_get и instance\_variable\_set. тем не менее, это редко оправдано и, как правило, является плохой идеей, так как обход инкапсуляции имеет тенденцию вызывать всевозможные разрушения.</p>