ruby的def 和 define_method的细致差别

ruby语言中,class_eval和instance_eval的具体差别比较明显, class_eval针对的是一个Class的对象,然后在此对象中可以定义instance方法。而instance_eval是针对某个对象,打开的class是 eigenclass或者singleton class。

class A

end

A.instance_eval do

def hello

puts "hello"

end

end

A.class_eval do

def world

puts "world"

end

end

此时,我们定义的是A的class方法,也就是说我们在A.eigenclass中定义了一个实例方法(instance method)"hello".

A.hello   # hello

A.world  # NoMethodError: undefined method `world‘ for A:Class

A.new.world # hello

到这一点为止,大家都感觉比较好明白。

现在下面的问题来了,

A.instance_eval do

define_method(:t1) do

puts "t1"

end

end

按照很多人说法,define_method和def机制类似的,只是def更高效,define_method是动态定义方法而已,并且define_method可以使用局部的变量。

所以,我们从上面分析,方法:t1 应该还是A的eigenclass的实例方法,或者说A的类方法。

A.t1  应该输出 "t1"

但实际是:

NoMethodError: undefined method `t1‘ for A:Class

A.new.t1 # t1

所以在instance_eval下,define_method 和 class_eval 是一致的,都是定义了A得实例方法。

而如果我们这样写,

class <<A

define_method(:t2) do

puts "t2"

end

end

A.t2 # t2

上面这种方法确实是在A.eigenclass中定义了实例方法 t2。

在这篇文章开头,我们说过,A.instance_eval 确实打开的时eigenclass, 为什么对def是正确地,而对define_method是不对的呢? 而在class << A 这种显示打开eigenclass时, define_method也是符合逻辑的。

或许是为了方便写meta方法,做了这种有意的调整。

klass.instance_eval
do

      method_object = instance_method(method)

define_method(method)
do
|*args, &block|

puts "==> calling #{method} with #{args.inspect}"

result = method_object.bind(self).call(*args,
&block)

puts "<== #{method} returned #{result.inspect}"

result

end

end

在上面这段代码里,对klass做了定义方法,而大部分这种写代码而言,动态生成方法都是想生成实例方法,而不是klass的类方法,所以,define_method在这个时候,做了特殊的变化,从而把我们先前总结的规律给引入了一个特殊的例外。如果不引入特殊的例外,这时,必须再加一个包装层, klass.class_eval do   ....  end 来做处理。

时间: 2024-08-26 08:19:34

ruby的def 和 define_method的细致差别的相关文章

ruby中的作用域和代码块

ruby中没有嵌套的作用域,它的作用域之间是分开的,一旦进入一个新作用域,原先的绑定就会被替换为一组新的绑定. 作用域门 一般来说,程序会在三个地方关闭前一个作用域,同时找开一个新的作用域. 它们分别是:class.module.def,它们被称为作用域门.class/module与def还有一点微妙的差别.在类或module中的代码会被立即执行.相反,方法中的代码只有在方法被调用的时候才执行. 扁平化作用域 my_var = "abc" class MyClass def my_me

Ruby学习-第二章

第二章 类继承,属性,类变量 1.如何声明一个子类 class Treasure < Thing 这样Thing类中的属性name,description都被Treasure继承 2.以下三种方式传入父类initialize方法的参数分别是什么? # This passes a, b, c to the superclass def initialize( a, b, c, d, e, f ) super( a, b, c ) end # This passes a, b, c to the s

ruby正则表达

1.Ruby中正则表达式的写法 主要有三种 在//之间,要进行转义 在%r{}内,不用进行转义 Regexp.new()内,不用进行转义 /mm\/dd/,Regexp.new("mm/dd"),%r{mm/dd}三者效果相同,实质都是新建了一个Regexp的类. 2.匹配的两种方法 =~肯定匹配, !~否定匹配.=~表达式返回匹配到的位置索引,失败返回nil,符号左右内容可交换 regexp#match(str),返回MatchData,一个数组,从0开始,还有match.pre_m

一个简单的ruby生成器例子(用连续体Continuation实现)

ruby中有很多经典的驱动器结构,比如枚举器和生成器等.这次简单介绍下生成器的概念.生成器是按照功能要求,一次产生一个对象,或称之为生成一个对象的方法.ruby中的连续体正好可以用来完成生成器的功能.连续体说起来晦涩,其实还是很简单的,它有3个特点: 1. callcc方法会给代码块传一个连续体对象,你可以保存该对象; 2. 当调用连续体的call方法时指令流会跳转到callcc方法之后; 3. 如果给连续体的call方法传递对象,则callcc方法会返回该对象,如果不传递对象,callcc会返

Ruby初探

官方网站:https://www.ruby-lang.org/zh_cn/ 标准库API文档:http://ruby-doc.org/stdlib-2.3.0/ 简介特性安装Ruby 命令行选项编码语法空白与行尾标识符与保留字Here DocumentBEGIN与END 语句注释运算符算数运算符比较运算符赋值运算符并行赋值位运算符范围运算符defined? 运算符点运算符 "." 和双冒号运算符 "::"运算符的优先级数据类型数值(Number)字符串(String

ruby regular Expressions

#!/usr/local/bin/ruby -w def show_regexp(a,re)   if a =~ re     "#{$`}<<#{$&}>>#{$'}"   else     "no match"   end end puts show_regexp('very interesting',/t/) puts show_regexp('Fats Waller',/a/)

ruby block each

#!/usr/local/bin/ruby -w # # # def call_each    animals = %w{ant bee cat dog elk}    for animal in animals      yield animal    end end call_each {|animal_block| puts animal_block}

Day 20 迭代器、生成器

一. 迭代器 1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代 (只能往后走不能往前退) 2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个iter()方法) 3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象 二.python中for循环机制 for循环的本质:循环所有对象,全都是使用迭代器协议. 正

Python:生成器

生成器是Python中的一个高级用法,有段时间我对生成器的理解颇为费劲,直到我看到一句话"yield语句挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行"后,让我恍然大悟,这是生成器中的状态挂起,这句话让我想起了在大学时玩ARM单片机时经常碰到的一个概念--中断,单片机在遇到中断信号时,处理中断程序前也要先保护现场,即系统要在执行中断程序之前,必须保存当前处理机程序状态字PSW和程序计数器PC等的值,待中断程序执行完成后在回复现场继续执行下面的程序.仔细想想,个人