【问题标题】:Overwriting attr_* methods in Ruby在 Ruby 中覆盖 attr_* 方法
【发布时间】:2013-01-28 03:15:58
【问题描述】:

我正在阅读这本书“The Well-Grounded Rubyist”,我突然想到了这个随机问题。我知道在 Ruby 中可以重新打开一个类并覆盖该方法。

例子:

class A
  def x
    "First definition of x"
  end

  def x
    "Second definition of x"
  end
end

test = A.new
test.x #returns "Second definition of x"

根据上面的结果,我很好奇是否可以用我自己的(随机)定义覆盖类方法attr_accessor。这是我的想法:

class Dummy
  attr_accessor :test

  def self.attr_accessor(method_name)
    puts "Overwrite the default functionality of attr_accessor by printing this text instead."
  end
end

d = Dummy.new
d.test #not sure why this returns nil instead of putting my message
Dummy.attr_accessor(test) #fails because of ArgumentError: wrong number of arguments (0 for 2..3)

对于上面的两个示例,我希望通过修补和提问来获得您的洞察力,从而更好地理解 Ruby。

【问题讨论】:

  • “重新打开一个方法并覆盖该方法是可能的”...呃...重新打开一个类并覆盖一个方法是可能的。
  • 抱歉术语混淆了;你是对的 - 我的意思是在一个类中可以有多个方法定义。谢谢!
  • 没关系。我只是在检查以确保您理解其中的区别。术语更正很重要,以免不了解差异的人阅读并得到错误的想法。现在,如果您覆盖旧定义会发生什么,然后调用使用该方法的代码,也许是您需要的其他一些 gem,它调用attr_accessor,期望它创建一个实例变量的 getter/setter 例程,而那些不由于您的覆盖而无法构建?这就是为什么我们非常小心覆盖核心例程的原因。只是想一想。

标签: ruby


【解决方案1】:

您走在正确的道路上,但类定义在运行时按顺序执行。您需要在调用它之前定义您的新方法,否则原始方法将用于该方法。

如果你这样做

class Dummy
  def self.attr_accessor(method)
    puts 'your message here'
  end
  attr_accessor :test # message printed
end

d = Dummy.new
d.test # raises an error, since we aren't creating a test method anymore
Dummy.attr_accessor(test) #will still fail, read on

test 是 Ruby 中的一个方法,对应于 shell 中的 test 内置函数。这种方法就是你最后得到错误的原因。你真正想要的是

Dummy.attr_accessor(:test1)

请注意,您将无法调用普通的attr_accessor,因为它是类定义中self 的类实例上的私有方法。 (IOW,attr_accessor是Module上的私有实例方法,在执行类定义时,self是Module的实例)

【讨论】:

  • 啊,好吧,所以我学到的第一件事是语句“attr_accessor :test”实际上是在调用我定义的attr_accessor方法。
  • 但是,我注意到运行“d.test1”会出现以下错误“NoMethodError: undefined method `test1'”。另一方面, Dummy.attr_accessor(:test1) 工作并返回:这是method_name:test1 => nil。不确定我是否真的得到了这个。还有其他指针吗?谢谢!
  • 如果你重写attr_accessor而不定义你自己的方法,或者调用super(调用原始方法),那么没有方法被创建。
  • -1: d.test 绝对不会打印消息。调用attr_accessor 时将打印该消息。 d.test. 会产生错误。
  • 当答案从错误和误导变为正确时,不赞成票很容易被撤销:-)
【解决方案2】:

是的,这是可能的,而你刚刚做到了!

d.test #not sure why this returns nil instead of putting my message

返回nil,因为您在下面重新定义之前使用了attr_accessor :test,因此,Ruby 执行attr_accessor 的默认行为并在Dummy 类中创建了一个成员和访问器。它返回nil,因为该成员的值未设置...nil。

Dummy.attr_accessor(test) #fails because of ArgumentError: wrong number of arguments (0 for 2..3)

失败不是你想的那样。此调用有效:

Dummy.attr_accessor("method_name") 

问题是您正在调用一个名为 test 的方法,但没有提供它的所有预期值。请参阅 Kernel.test() http://www.ruby-doc.org/core-1.9.3/Kernel.html#method-i-test 的文档。

您看到的错误消息是因为您错误地调用了测试方法,而不是因为您对 attr_accessor 的重新定义有误。

【讨论】:

    【解决方案3】:

    在您的Dummy 类中,调用attr_accessor 的Class 版本是因为在调用attr_accessor 时尚未定义Dummy 版本。如果在调用之前移动定义,行为会有所不同。

    class Dummy    
      def self.attr_accessor(method_name)
        puts "Overwrite the default functionality of attr_accessor by printing this text instead."
      end
    
      attr_accessor :test
    end
    

    这将在读取类定义时打印您的消息。当您尝试在 Dummy 对象上调用方法 test 时,您会收到错误消息,因为没有定义此类方法。

    【讨论】:

      【解决方案4】:

      如果您颠倒您的attr_accessor 呼叫和您的def self.attr_accessor 的顺序会发生什么?我不知道这是否会解决它,但你当然不能按照你的顺序去做。请记住,当您打开带有class Dummy 之类的行的类时,您正在实时执行真实代码。所以首先你正在调用attr_accessor,然后你正在尝试重新定义它。好吧,我不知道你是否可以这样重新定义它,但你肯定需要在调用它之前尝试重新定义它,否则你将调用旧版本!

      【讨论】:

      • 事实上,我已经写了一个关于 Ruby 的简短介绍;您没有掌握 attr_accessor 是一个方法调用或 def 是可执行代码(并且这些事情是按顺序发生的)这一事实表明您可能会从中得到一些东西,因为它对那种事情:apeth.com/rubyIntro/justenoughruby.html
      • 感谢@matt,我已经开始阅读这篇文章并希望从您的详尽解释中了解更多信息!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-14
      • 1970-01-01
      • 1970-01-01
      • 2012-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多