Ruby基础语法(1): Object

原文  http://blog.sloger.info/programing/basic-grammar-in-ruby-part-1-obje

很早就想学习除了 Perl 以外的另一门脚本语言,自然而然最近比较流行的就是 Python 和 Ruby,之前也曾纠结过要学哪一个。也曾每种语言都浏览一下基本语法,发现 Ruby 与 Perl 在一些方面挺像,再加上群里有人说学一门语法糖多的语言对以后有好处,于是便决定学习 Ruby 了。

本文本来是比较 Ruby 与 Perl 的语法的,结果发现越写越长,于是干脆不比较了,只是在一些地方提到了 Perl 的做法。

Ruby 与 Perl 有很多相似的地方。包括但不限于:

  • $ + 一个标点符号的预定义全局变量
  • 相同的命令行参数
  • 嵌入语言的正则引擎

演示均在 irb 中进行,普通行表示输入, # 开头的行表示输出,#=> 开头的行表示返回值

Ruby 中一切皆是对象

1.class
#=> Fixnum
1.1.class
#=> Float
4200000000.class
#=> Bignum
[].class
#=> Array
{}.class
#=> Hash
/1/.class
#=> Regexp
nil.class
#=> NilClass
Kernel.class
#=> Module
Module.class
#=> Class
Class.class
#=> Class

Blocks

为什么把 block 放在最前面?因为 block 在 ruby 中的地位相当重要,后面要讲的内容全都涉及到了 block

Ruby中, { }do end 之间的代码集合构成一个 block。

调用方法时可以添加一个 block ,方法中可以使用 yield 来执行关联的 block。

{ }do end 的优先级高,通常的做法是,单行用 { } ,多行用 do end

Methods

方法定义

Ruby 中使用 def + 方法名称 + 可选的参数列表 + 方法体 + end 定义方法。

# 建议:有参数时添加括号,无参数时省略括号
def Method_1(arg1, arg2)
  "#{arg1.capitalize} #{arg2.capitalize}"
end
#=> :Method_1

Method_1("hello", "ruby")
#=> "Hello Ruby"

# 方法的参数可以有默认的取值
def Method_2(arg1 = "Hello", arg2 = "ruby")
  "#{arg1} #{argv2}"
end
#=> :Method_2

def 的返回值是方法名对应的 Symbols ,以后不再列出。

方法可以随意的重新定义,而不会出现其它语言中的方法重定义错误。

def welcome
  "Hello Ruby"
end
#=> :welcome

welcome
#=> "Hello Ruby"

def welcome
  Hello Ruby ” * 2
end
#=> :welcome

welcome
#=> "Hello Ruby Hello Ruby "

特殊的方法名

Ruby 方法可以以三种标点符号结尾

  • ? : 表示询问的方法,返回布尔值
  • ! : 危险的方法,或者会修改调用者本身 (并不适用于所有情况)
  • = : 出现在类里,以 = 结尾的方法可以出现在赋值语句的左边

可变参数方法

def varargs(arg1, arg2, *rest)
    "Args: #{arg1}, #{arg2}, #{rest.join(', ')}"
end

*rest 表示将实参列表中除了 arg1arg2 之外的其余参数收集成为一个 Array 并赋值给 rest

Method with Block

def welcome(greeting = "Hello Ruby")
  if block_given?
    yield
  else
    greeting
  end
end

welcome
#=> "Hello Ruby"
welcome { "Welcome to the Ruby world" }
#=> "Welcome to the Ruby world"

block_given? 用于判断调用方法时是否提供了关联的Block, 与 Perl 中的 wantarray 有异曲同工之处。

yield 用来调用调用方法时提供的Block,可与方法调用一样给 yield 添加参数。

& 修饰符

  • 如果方法的最后一个形参前有 & 修饰符,调用方法时将寻找一个 block 并将其转为一个 Proc 对象并赋值给该参数。
def welcome(&proc)
  proc.call unless proc.nil?
end
#=> :welcome

welcome
#=> nil
welcome { puts "Hello Ruby" }
# Hello Ruby
#=> nil
  • 也可以使用 lambda 显式地将一个 block 转换成为 Proc 对象。
def welcome(proc)
  proc.call unless proc.nil?
end

welcome(lambda { puts "Hello Ruby" })
# Hello Ruby
=> nil
  • 如果调用方法的最后一个实参前有 & 修饰符,则认为其是一个 Proc 对象,将其从实参中删除并转换为 block。
def welcome
  yield if block_given?
end
#=> :welcome

welcome
#=> nil

greeting = lambda { puts "Hello Ruby" }
#=> #<Proc:0x94e2290@(pry):166 (lambda)>

welcome &greeting
#Hello Ruby
#=> nil

收集散列表

不说了,看吧

def hash_collect(arg1, rest)
  puts  "#{arg1} #{rest}"
end

hash_collect("f", { 'A' => 'x', 'B' => 'y'})
# f {"A"=>"x", "B"=>"y"}
# => nil

hash_collect("f", 'A' => 'x', 'B' => 'y')
# f {"A"=>"x", "B"=>"y"}
# => nil

hash_collect("f", :A => :x, :B => :y)
# f {:A=>:x, :B=>:y}
#=> nil

Array

Array的定义

直接初始化

a = [1, 2, 4, 6, 8, "asd"]
#=> [1, 2, 4, 6, 8, "asd"]
lang = %w( perl python ruby java lisp )
#=> ["perl", "python", "ruby", "java", "lisp"]

显式使用 Array.new

Array.new 有3种参数形式

# size 为大小, obj为填充的对象
Array.new(size = 0, obj = nil)
# 用另一个 Array 初始化一个 Array
Array.new(another_array)
# 定义一个大小为 size 的 Array,并使用 index 调用 block,使用其返回值填充 Array
Array.new(size) { |index| block }

比如:

Array.new(5, "Ruby")
#=> ["Ruby", "Ruby", "Ruby", "Ruby", "Ruby"]
Array.new(Array.new(5, "Ruby"))
#=> ["Ruby", "Ruby", "Ruby", "Ruby", "Ruby"]
Array.new(5) { |index| index ** 2 }
#=> [0, 1, 4, 9, 16]

* 修饰符

  • * 用于等式的左边(方法的形参)时,将等式的右边(方法的实参)中剩余的部分收集成一个 Array。
  • * 用于等式的右边(方法的实参)时,将等式的右边(方法的实参)中对应的变量当成一个 Array 并分解。
a = (1 .. 5).to_a
#=> [1, 2, 3, 4, 5]

# b == 1,  c == 2
b,  c = a
#=> [1, 2, 3, 4, 5]

# b == 1,  c == [2, 3, 4, 5]
b, *c = a
#=> [1, 2, 3, 4, 5]

# b == 99, c == [1, 2, 3, 4, 5]
b,  c = 99, a
#=> [99, [1, 2, 3, 4, 5]]

# b == 99, c == [[1, 2, 3, 4, 5]]
b, *c = 99, a
#=> [99, [1, 2, 3, 4, 5]]

# b == 99, c == 1
b,  c = 99, *a
#=> [99, 1, 2, 3, 4, 5]

# b == 99, c == [1, 2, 3, 4, 5]
b, *c = 99, *a
#=> [99, 1, 2, 3, 4, 5]

Array 的访问

Ruby 可以使用 [] 访问 Array 中的元素, [] 有3中参数形式:

  • lang[index] & lang.slice(index)
  • lang[start, length] & lang.slice(start, length)
  • lang[range] & lang.slice(range)

举例:

lang[0]
#=> "perl"

# 访问不存在的元素
lang[100]
#=> nil

# 逆序访问
lang[-1]
#=> "lisp"

# 直接赋值
lang[6] = "php"
#=> "php"

# 自动填充
lang
#=> ["perl", "python", "ruby", "java", "lisp", nil, "php"]

lang[3, 4]
#=> ["java", "lisp", nil, "php"]

lang[2..5]
#=> ["ruby", "java", "lisp", nil]

Array 运算

# 相加
%w( 1 1 2 3 4 ) + %w( 2 4 5 )
#=> ["1", "1", "2", "3", "4", "2", "4", "5"]

# 相减
%w( 1 1 2 3 4 ) - %w( 2 4 5 )
#=> ["1", "1", "3"]

# 乘以 int 表示重复
%w(1 2 3 4) * 2
#=> ["1", "2", "3", "4", "1", "2", "3", "4"]

# 乘以 string 表示 join
%w(1 2 3 4) * ", "
#=> "1, 2, 3, 4"

# 相与并去重
%w(1 1 2 3 4) & %w( 2 4 5 )
#=> ["2", "4"]

# 后入 ...
[1, 2] << "c" << "d" << [3, 4]
#=> [1, 2, "c", "d", [3, 4]]

# 相等 大小相等 每个元素也相等
%w(1 1 2 3 4) == %w( 2 4 5 )
#=> false

其它常用的 Array 方法

# 清空 lang
lang.clear

# 去重
%w(1 1 2 3 4).uniq
#=> ["1", "2", "3", "4"]

lang.join(', ')
#=> "perl, python, ruby, lisp, php"

lang.size
#=> 5
lang.length
#=> 5

lang.empty?
#=> false
lang.include?('python')
#=> true
lang.include?('java')
#=> false

# 转置
lang.reverse
#=> ["php", "lisp", "ruby", "python", "perl"]
lang.reverse!

lang.sort
#=> ["lisp", "perl", "php", "python", "ruby"]
lang.sort!

# 删除指定元素
lang.delete('java')
#=> "java"
lang
#=> ["perl", "python", "ruby", "lisp", "php"]

Array 迭代器

# 返回第一个符合要求的元素
[1, 2, 3, 5, 7, 8].find { |index| index > 6 }
#=> 7

lang.each { |l| puts l.capitalize }
# Perl
# Python
# Ruby
# Lisp
# Php
#=> ["perl", "python", "ruby", "lisp", "php"]

# 使用 lang 的每个元素调用 block,以 block 的返回值生成新的 Array
lang.collect { |l| l.to_s.capitalize }
#=> ["Perl", "Python", "Ruby", "Java", "Lisp", "Php"]
# 同上,但是直接修改 lang
lang.collect! { |l| l.to_s.capitalize }

# map 同 collect
lang.map { |l| l.to_s.capitalize }
lang.map! { |l| l.to_s.capitalize }

# 去除 lang 中的 nil 元素
lang.compact
#=> ["perl", "python", "ruby", "java", "lisp", "php"]
# 直接修改自身
lang.compact!

# 使用上一次迭代的返回值和下一个元素调用block,一个可选的参数用于指定初始的迭代值
[1, 2, 3, 5, 7, 8].inject { |sum, ele| sum + ele }
#=> 26
[1, 2, 3, 5, 7, 8].inject { |fab, ele| fab * ele }
#=> 1680

更多 Array API 参阅 Ruby-Doc Array

Range

Range 的定义

  • .. 表示闭区间
  • ... 表示左闭右开区间
1 .. 5
#=> 1..5
1 ... 5
#=> 1...5
(1 .. 5).to_a
#=> [1, 2, 3, 4, 5]
(1 ... 5).to_a
#=> [1, 2, 3, 4]

Range 做为布尔值

在 Range A .. B 中,A 之前为 false , A 到 B 为 true ,B 之后为 false

Range 成员方法

a = 1 .. 5
=> 1..5

a === 4
#=> true
a.include? 4
#=> true

a === 'z'
#=> false
a.include? 'z'
#=> false

a.max
#=> 5
a.min
#=> 1

a.each { |e| print "#{e} " }
# 1 2 3 4 5 #=> 1..5

a.reject { |e| e > 3 }
#=> [1, 2, 3]

更多 Range API 参阅 Ruby-Doc Range

Hash

Hash 定义

直接定义

像 Perl 一样,Ruby 使用大括号 + “胖箭头” ( => ) 定义 Hash

hash = { 'a' => 'x', 'b' => 'y', 'c' =>'z' }
#=> {"a"=>"x", "b"=>"y", "c"=>"z"}

hash = { :a => 'x', :b => 'y', :c =>'z' }
#=> {:a=>"x", :b=>"y", :c=>"z"}

显示使用构造函数

像 Array 一样,Hash 的构造函数也有三种形式,含义与 Array.new 相同

  • new
  • new(obj)
  • new {|hash, key| block }

Hash 访问

与 Perl 不同的是,Hash 使用 [] 访问,而非 {}

hash[:a]
#=> "x"
hash[:b]
#=> "y"
hash[:d]
#=> nil

Hash 常用方法

# 长度
hash.length
#=> 3

# 清空。这个就不演示了
hash.clear

# 是否为空
hash.empty?
#=> false

# 删除。如果给了 block,则不存在键时会把参数传递给 block。
# 成功时返回被删除的值,失败时返回 nil。
hash.delete(:d)
#=> nil

hash.delete(:d) {|key| puts "#{key} not found" }
# d not found
#=> nil

# 获取值对应的键
hash.key('y')
#=> :b
hash.key('m')
#=> nil

# 是否存在键
hash.key?(:a)
#=> true
hash.key?(:d)
#=> false

# 返回所有的键
hash.keys
#=> [:a, :b, :c]

# 转为 Array
hash.to_a
#=> [[:a, "x"], [:b, "y"], [:c, "z"]]

Hash 迭代器

hash.each {|key, value| block }

# 同上
hash.each_pair {|key, value| block }

hash.each_key {|key| block }
hash.each_value {|value| block }

# 删除掉 block 返回 true 的键值
hash.reject {|key, value| block}

更多 Hash API 参阅 Ruby-Doc Hash

Regexp

正则表达式的语法不再介绍了 ~~~

Pattern 的定义

Ruby 像 Perl 一样正则表达式引擎是直接嵌入语言的。

# 直接赋值
a = //

# perl 中的 qr/ /
b = %r()

# 面向对象做法
b = Regexp.new()

匹配

类似 Perl,Ruby 中有 =~ 表示匹配, !~ 表示不匹配。

$`                                    # 匹配之前的部分
$&                                    # 匹配的部分
$'                                    # 匹配之后的部分
$1 $2 $3 $4                           # 捕获组
\1 \2 \3 \4                           # 反向引用

替换

Ruby 中没有 Perl 中的 s/pattern/string/ 替换。而是使用 string 的 subgsub 方法。

subgsub 有两种形式:

  • sub(pattern, string) : 表示用 string 替换 pattern。
  • sub(pattern) { |match| block } : 表示将匹配部分传递给 block,并用其返回值替换 pattern。

gsubsub

Regexp 对象

re = %r(\b\d+\b)
#=> /\b\d+\b/
re.class
#=> Regexp

# 匹配的结果是一个 MatchData 对象
md = re.match('abcd 1234 ABCD')
#=> #<MatchData "1234">
md.class
#=> MatchData

# 访问
md[0]                                # $&
#=> "1234"
md[1]                                # $1
#=> nil
md.pre_match                         # $`
#=> "abcd "
md.post_match                        # $'
#=> " ABCD"

与嵌入语言的正则表达式相关变量相比,面向对象的匹配可以同时保存多组 MatchData 对象。

$ $& $' $1 $2 等都是从线程局部变量 $~ 中获取的,因此可以通过修改 $~ 的值来访问不同的匹配结果

$~ 默认总是最后一个匹配的 MatchData 对象。

md1 = re.match('linux 0987 unix')
#=> #<MatchData "0987">
md1.object_id
#=> 78126770

# $~ 是最后一次匹配结果的 MatchData 对象的引用
$~
#=> #<MatchData "0987">
$~.class
#=> MatchData
$~.object_id
#=> 78126770
"#$` #$& #$'"
#=> "linux  0987  unix"

# 一次新的匹配
md2 = re.match('perl 3256 ruby')
#=> #<MatchData "3256">
md2.object_id
#=> 78163110
"#$`-#$&-#$'"
#=> "linux -0987- unix"

# 默认引用最后一次匹配的结果
$~
#=> #<MatchData "3256">
"#$`-#$&-#$'"
#=> "perl -3256- ruby"

# 修改 $~ 访问上一次匹配结果
$~ = md1
#=> #<MatchData "0987">
"#$`-#$&-#$'"
=> "linux -0987- unix"

# 修改回来,访问最后一次匹配
$~ = md2
#=> #<MatchData "3256">
"#$`-#$&-#$'"
#=> "perl -3256- ruby"

Class

定义类和实例

class Person
  def initialize(name = "", age = 0, gender = "male")
    @name   = name
    @age	= age
    @gender = gender
  end
end
#=> :initialize
p = Person.new("Ruby", 19)
# 返回 p.inspect 的返回值
#=> #<Person:0x8d8a90c @name="Ruby", @age=19, @gender="male">
p.to_s
#=> "#<Person:0x8d8a90c>"
p.inspect
#=> "#<Person:0x8d8a90c @name=\"Ruby\", @age=19, @gender=\"male\">"

class 的返回值是定义的最后一个方法名对应的 Symbols

访问类成员

方法使用 . 访问,常量使用 :: 访问。

重写类方法

Ruby 中的类可以像方法一样重新打开并添加方法。

# 重新打开并添加成员方法
class Person
  def inspect
    puts self.to_s
   end
   def to_s
     "#{self.class}: #@name, #@age, #@gender"
  end
end
#=> :to_s
p
# p.inspect
# Person: Ruby, 19, male
#=> nil
p.to_s
#=> "Person: Ruby, 19, male"

实例特定的方法

p = Person.new
#=> #<Person:0x890cf10>
def p.say
  puts "Hello Ruby"
end
#=> :say
p.say
# Hello Ruby
#=> nil
p2 = Person.new
#=> #<Person:0x8b26b20>
p2.say
# NoMethodError: undefined method `say' for #<Person:0x8b26b20>
#		 from (irb):10
#		 from /usr/bin/irb:11:in `<main>'

类变量,类方法

class Person
  # 类变量
  @@num = 0

  # 类方法,同 def Person.empty?
  def self.empty?
    @@num == 0
  end

  def initialize(name = "", age = 0, gender = "male")
    @name, @age, @gender = name, age, gender
    @@num += 1
  end
end
#=> :initialize
Person.empty?
#=> true
Person.new
#Person: , 0, male
#=>
Person.empty?
#=> false

继承

class Student < Person
  def initialize(name = "", age = 0, gender = "male", scole = 0)
    super(name, age, gender)				# 同 java super 用法
    @scole = scole
  end
  def to_s
    super + " (#@scole)"					# 非构造函数也能用
  end
end
#=> :to_s
s = Student.new("Perl", 29, "male", 90)
#=> "Student: Perl, 29, male"
s.to_s
#=> "Student: Perl, 29, male (90)"

super()super 的区别:

  • super() 表示不加参数调用超类同名方法
  • super 表示已调用此方法的参数调用超类同名方法

attr

Ruby 中的 attr_reader & attr_writer & attr_accessor 用来添加 setter & getter

class Student; end
#=> nil

# 使用反射查看类不包括继承来的实例方法
Student.instance_methods false
#=> []

class Student
  attr_reader :name
  attr_writer :age
  attr_accessor :gender
end
#=> nil

# 自动添加了 setter getter
Student.instance_methods false
#=> [:name, :age=, :gender, :gender=]

cattr

同 attr,但是添加的是类方法,而非实例方法

Student.methods false
#=> []

require 'active_support/all'
#=> true

class Student
  cattr_reader :total
  cattr_accessor :school
end
#=> [:school]

Student.methods false
#=> [:total, :school, :school=]

打开内置类

Ruby 中可以打开内置的类并添加,修改方法。

array = (1 .. 5).to_a
#=> [1, 2, 3, 4, 5]

array.fabs if array.methods.include? 'fabs'
#=> nil

class Array
  # 阶乘
  def fact
    inject { |fab, ele| fab * ele }
  end
end
#=> :fact

array.fact
#=> 120

也可以在改变内置类的方法时,创建一个别名。

# 修改内置的 Fixnum 类
class Fixnum
  alias old_plus +

  def +(other)
    old_plus(other) * 2
  end
end

1 + 1
#=> 4
1.old_plus 1
#=> 2

访问权限

Ruby 中有三个方法: public protected private 可以像 C++ 那样控制成员的访问权限。

class Access
  # 默认为 public
  def pub_m;  end
private
  def pri_m;  end
protected
  def pro_m;  end
public
  def pub_m2; end
end

Access.public_instance_methods false
#=> [:pub_m, :pub_m2]
Access.private_instance_methods false
#=> [:pri_m]
Access.protected_instance_methods false
#=> [:pro_m]

也可以使用方法名作为参数传递给这三个函数。

class Control
  def pub_m; end
  def pro_m; end
  def pri_m; end

  # 要放到方法定义之后,ruby需要看到方法的定义
  public	:pub_m
  private   :pri_m
  protected :pro_m
end

Control.public_instance_methods false
#=> [:pub_m]
Control.protected_instance_methods false
#=> [:pro_m]
Control.private_instance_methods false
#=> [:pri_m]

Modules

模块就像一个 namespace

一个模块定义了一个 namespace,这样可以防止变量名、方法名污染

Modules 定义

module Modules
  def Modules.m_1
    puts "Method Modules.m_1"
  end

  def self.m_2
    puts "Method self.m_2"
  end

  def m_3
    puts "Method m_3"
  end
end

Modules.m_1
# Method Modules.m_1
#=> nil

如果要引用其它的模块,使用 require 'Module_Name'

Mixin: 模块又像一个 Interface

Mixin 容许一个 class inlcude 一个 module

class Class_M
  include Modules
end


# 只有实例方法被 Mixin 了
Class_M.instance_methods
#=> [:m_3, :nil? ... ]

Class_M.new.m_3
# Method m_3

改变被 Mixin 的模块之后,所有 include 该模块的类都会发生变化.

module Modules
  def m_3
    puts "#{self.class}: Method m_3"
  end
end

# 注意 self.class 是 Class_M 而非 Modules
Class_M.new.m_3
# Class_M: Method m_3

可以使用 Some_Class.included_modules 来获取类 include 的模块。

Class_M.included_modules
#=> [Modules, Kernel]

可以看到除了 Modules 之外,还有一个 Kernel 模块。

Kernel Modules

其实 KernelObject inlucde 的,而所有对象的最终 superclass 都是 Object ,于是所有的类就都 include Kernel 了。

Object.included_modules
#=> [Kernel]
Object.superclass
#=> BasicObject
BasicObject.included_modules
#=> []

Ruby 中大部分表现的像是语言内置的方法其实都是定义在 Kernel 中的,比如:

Kernel.methods
#=> [:sprintf, :format, :Integer, :Float, :String, :Array, :Hash, :warn, :raise, :fail, :global_variables, :__method__, :__callee__, :__dir__, :eval, :local_variables, :iterator?, :block_given?, :catch, :throw, :loop, :trace_var, :untrace_var, :at_exit, :syscall, :open, :printf, :print, :putc, :puts, :gets, :readline, :select, :readlines, :`, :p, :test, :srand, :rand, :trap, :exec, :fork, :exit!, :system, :spawn, :sleep, :exit, :abort, :load, :require, :require_relative, :autoload, :autoload?, :proc, :lambda, :binding, :caller, :caller_locations, :Rational, :Complex, :set_trace_func, :freeze, :===, :==, :<=>, :<, :<=, :>, :>=, :to_s, :inspect, :included_modules, :include?, :name, :ancestors, :instance_methods, :public_instance_methods, :protected_instance_methods, :private_instance_methods, :constants, :const_get, :const_set, :const_defined?, :const_missing, :class_variables, :remove_class_variable, :class_variable_get, :class_variable_set, :class_variable_defined?, :public_constant, :private_constant, :singleton_class?, :include, :prepend, :module_exec, :class_exec, :module_eval, :class_eval, :method_defined?, :public_method_defined?, :private_method_defined?, :protected_method_defined?, :public_class_method, :private_class_method, :instance_method, :public_instance_method, :nil?, :=~, :!~, :eql?, :hash, :class, :singleton_class, :clone, :dup, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :extend, :display, :method, :public_method, :singleton_method, :define_singleton_method, :object_id, :to_enum, :enum_for, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]

Mixin 的应用

当编写自定义的类时,可以 Mixin 一些内置的 Module。

  • Comparable : Mixin 这个模块,再定义 <=> 方法,就免费获得了 < <= == >= >between? 方法。
  • Enumerable : Mixin 这个模块,再定义 each 迭代器,就获得了 map include? find_all 等迭代器,若果再定义 <=> 方法,还可以获得 min max sort 等方法。

方法的查找

调用方法时会按照一定的顺序查找方法:

对象直属类 > 类 Mixin 的 Module > 类的超类 > 累的超类 Mixin 的 Module > ...

如果一个类 Mixin 了多个模块,则按照 Mixin 的反向顺序查找,即最后一个 include 的 Module 会最先被查找。

实际上是按照一个 Array 查找的:

Class_M.ancestors
#=> [Class_M, Modules, Object, Kernel, BasicObject]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值