深入理解 Ruby 中的 instance_eval 和 class_eval

Ruby 通过 eval 可以将字符串当做代码来执行,而执行环境是通过 binding 进行绑定的;除此之外,Ruby 还有另外两个方法:instance_eval 和 class_eval, 用来执行代码块。后者的执行上下文与调用者相关。

instance_eval: 调用者必须是实例 instance;
class_eval: 调用者必须是类 class;

下面的例子帮助识别两个方法的区别:

[1] pry(main)> class Hello
[1] pry(main)*   def self.say_c
[1] pry(main)*     puts "Hello self.say"
[1] pry(main)*   end  
[1] pry(main)*   def say_i
[1] pry(main)*     puts "Hello lsay"
[1] pry(main)*   end  
[1] pry(main)* end  

在 pry 交互环境中,先定义一个 Hello 类,有两个方法,一个类方法 say_c, 一个实例方法 say_i。

[2] pry(main)> ls Hello
Hello.methods: say_c
Hello#methods: say_i

分别通过 Hello.instance_eval 和 Hello.class_eval 定义两个新方法: foo_i, foo_c

[4] pry(main)> Hello.instance_eval do 
[4] pry(main)*   def fool_i
[4] pry(main)*     puts "Hello instance_eval foo_i"
[4] pry(main)*   end  
[4] pry(main)* end  
=> nil
[5] pry(main)> Hello.class_eval do
[5] pry(main)*   def foo_c
[5] pry(main)*     puts "Hello class_eval foo_c"
[5] pry(main)*   end  
[5] pry(main)* end  
=> nil

查看 Hello 的方法变化:

[6] pry(main)> ls Hello
Hello.methods: foo_i  say_c
Hello#methods: foo_c  say_i

有上面的结果可见,instance_eval 代码块中定义的方法变成了 Hello 的类方法,而 class_eval 代码块中
定义的方法称为了 Hello 类的实例方法。这里有点绕,跟语义上边理解起来的相反。

[7] pry(main)> h1 = Hello.new
=> #<Hello:0x007ff978fc5458>
[8] pry(main)> h1.instance_eval do 
[8] pry(main)*   def cry
[8] pry(main)*     puts "h1 cry"
[8] pry(main)*   end  
[8] pry(main)* end  
=> nil
[9] pry(main)> ls h1
Hello#methods: foo_c  say_i
self.methods: cry

h1 为 Hello 类的实例,h1.instance_eval 中定义的方法成为了 h1 的单例方法(类似于java中类的静态方法),也就是说只有 h1 能够调用的方法。

总结:

  • h1 不能调用 class_eval;
  • h1 调用 instance_eval 的作用域为对象实例本身;其中定义的方法为单例方法;
  • Hello 调用 class_eval 的作用域为类本身;其中定义的方法为实例方法;
  • Hello 调用 instance_eval 的作用域为 class 对象,其中定义的方法为类方法;
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ast.literal_eval是一个函数,可以将字符串转换为对应的Python对象,例如将字符串'123'转换为整数123,将字符串'[1, 2, 3]'转换为列表[1, 2, 3]。它可以安全地解析一些简单的Python表达式,但不支持所有Python语法。使用时需要注意安全性,避免执行恶意代码。 ### 回答2: ast.literal_eval是Python内置的一个函数,它用于安全地评估一个字符串类型的Python表达式,并返回表达式的值。 使用ast.literal_eval可以避免使用内置函数eval()可能引发的安全问题。eval()函数会将传入的字符串当作Python代码进行评估,如果字符串来自不可信的来源,可能会导致执行恶意代码。而ast.literal_eval会执行更严格的评估,只会允许对字面值表达式的评估,不会执行任何函数调用或表达式语句。 ast.literal_eval的使用非常简单,只需将一个合法的字符串表达式作为参数传入即可。如果字符串的语法不正确, ast.literal_eval会抛出一个值错误(SyntaxError);如果字符串包含的表达式不是有效的Python字面值,会抛出一个值错误(ValueError)。如果表达式的评估成功,ast.literal_eval会返回表达式的值。 例如,假设有一个字符串'1 + 2',如果直接用eval()函数进行评估,会抛出一个错误,因为'1 + 2'不是一个有效的Python表达式。但如果使用ast.literal_eval('1 + 2'),会抛出一个值错误,提示无效的字面值。只有当传入合法的字面值表达式时,ast.literal_eval才能正确评估并返回其值。 总之,ast.literal_eval是一个非常有用的函数,它提供了一种安全且简便的方法来评估字符串类型的Python表达式,帮助我们在处理可信和不可信的Python代码时,减少潜在的安全风险。 ### 回答3: ast.literal_eval是一个Python内置模块ast的函数,用于将字符串形式的字面值转换为对应的Python对象。它类似于内置函数eval,但有一些重要差异。 使用ast.literal_eval时,传入的参数必须是合法的Python字面值表达式,例如数字、字符串、元组、列表、字典、布尔值和None值等,否则会引发SyntaxError异常。 相比于eval函数,ast.literal_eval更加安全。因为它仅仅被用于求值字面量表达式,不会执行任何表达式的函数或变量,因此可以避免恶意代码的执行。这使得ast.literal_eval成为处理不可信输入时的一个很好的选择。 ast.literal_eval的返回值是根据传入的参数的类型而变化的,如果是字面值表达式,返回值将是对应的Python对象。例如,传入的字符串是"[1, 2, 3]",则返回一个列表 [1, 2, 3]。如果传入的字符串无法解析成合法的Python字面值,将会引发ValueError异常。 ast.literal_eval的应用场景广泛。例如,当从文件读取字符串表示的数据时,可以使用ast.literal_eval将其转换为相应的数据结构。此外,它还常用于处理JSON数据,因为JSON的数据类型与Python的数据类型非常相似,可以使用ast.literal_eval将JSON字符串转换为Python对象。 总而言之,ast.literal_eval是一个安全且高效的函数,用于将字符串形式的字面值转换为Python对象。在处理不可信输入和解析字符串数据时,ast.literal_eval可以帮助我们避免潜在的安全问题,并提供更好的代码性能和可读性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值