Ruby中的类和模块和变量使用说明
对象, 变量, 常量和类
在ruby中表现数据的基本单位称为对象。
常见对象与其所属的类
对象 | 类 |
---|---|
数值 | Numeric |
字符串 | String |
散列 | Hash |
正则表达式 | Regex |
文件 | File |
符号 | Symbol |
数组 | Array |
此外还有range和execption对象等
变量分为以下几种:
- 全局变量: 以$来表示
- 实例变量: 以@开头来表示
- 类变量: 以@@开头来表示
- 局部变量: 以英文字母或者_来表示
除了以上几种变量类型 还有伪变量类型(一种在ruby里面预先定义的变量类型)
Ruby中,nil、true、false、self等都是伪变量
全局变量与局部变量示例
新建两个文件,一个为scopetest.rb, 一个为variable.rb
variable.rb内容
$x = 1
x = 1scopetest.rb内容
$x = 200
require_relative 'sub'x = 3
p $x
p x
结果为:1 3
常量以大写字母命名
且一旦赋值以后就不在可以被更改,如果赋值会产生异常。
ruby中的保留字
LINE | ENCODING | FILE | BEGIN |
---|---|---|---|
END | alias | and | begin |
break | case | def | defined? |
do | else | elsif | end |
ensure | false | for | while |
if | in | module | next |
nil | not | or | redo |
rescue | retry | return | self |
super | then | true | undef |
unless | until | when | while |
yield |
类和模块
当想知道某个对象属于哪个类时,我们可以使用class方法ary = []
str = "hello world"
p ary.class #=> Array
p str.class #=> String当想知道某个对象是否属于某个类时,我们可以使用instance_of?方法
ary=[]
str="Helloworld."
p ary.instance_of?(Array) #=>true
p str.instance_of?(String) #=>true
p ary.instance_of?(String) #=>false
p str.instance_of?(Array) #=>false
继承
通过扩展已定义的类来创建新类称为继承。
继承后创建的新类称为子类(subclass),被继承的类称为父类2(superclass)。通过继承可以实现以下操作。
- 在不影响原有功能的前提下追加新功能
- 重新定义原有功能, 使名称相同的方法产生不同的效果
- 在已有功能的基础上追加处理,扩展已有功能
BasicObject类是Ruby中所有类的父类,它定义了作为Ruby对象的最基本功能。备注BasicObject类是最最基础的类,甚至连一般对象需要的功能都没有定义。因此普通对象所需要的类一般都被定义为Object类。字符串、数组等都是Object类的子类。子类与父类的关系称为“isa关系”3。例如,String类与它的父类Object就是isa关系。
str = "hello" ;
p str.is_a?(String) #=> true
p str.is_a?(Object) #=> true
p str.is_a?(BasicObject) #=> true
由于instance_of?方法与is_a?方法都已经在Object类中定义过了,因此普通的对象都可以使用这两个方法。示例:创建一个类
class HelloWorld # class 语句def initialize(myname = "Ruby") # 类方法@name = myname # 初始化实例变量enddef hello # 实例方法puts "hello, world. I am #{@name}"end
endbob = HelloWorld.new("Bob")
alice = HelloWorld.new("Alice")
ruby = HelloWorld.new("Ruby")
bob.hello
alice.hello
ruby.helloclass 类名
类的定义
end类名的首字母必须大写。名为initialize的方法比较特别。使用new方法生成新的对象时,initialize方法会被调用,同时new方法的参数也会被原封不动地传给initialize方法。因此初始化对象时需要的处理一般都写在这个方法中。而只要在同一个实例中,程序就可以超越方法定义,任意引用、修改实例变量的值。另外,引用未初始化的实例变量时的返回值为nil。
存取器
在Ruby中,从对象外部不能直接访问实例变量或对实例变量赋值,需要通过方法来访问对象的内部。
对类进行扩充,增加存取方法:class HelloWorld # class 语句def initialize(myname = "Ruby") # 类方法@name = myname # 初始化实例变量enddef hello # 实例方法puts "hello, world. I am #{@name}"enddef name=(name) # 存@name = nameenddef name # 取return @nameend
endbob = HelloWorld.new("Bob")
bob.hellobob.name =("kobe")
p bob.name
bob.name =("kobe") #=> 实例变量@name的值此时为kobe
p bob.name
当对象越来越多时,需要定义的存取器越来越多,导致代码越来越复杂。因此ruby提供了存取器的快捷方式
定义 | 意义 |
---|---|
attr_reader :name | 只读(定义name方法) |
attr_writer :name | 只写(定义 name=方法) |
attr_accessor :name | 读写(定义以上两个方法) |
在实例方法中,可以用self这个特殊的变量来引用方法的接收者class HelloWorld # class 语句attr_accessor :namedef initialize(myname = "Ruby") # 类方法@name = myname # 初始化实例变量enddef greetputs "Hi, I am #{self.name}"enddef greet_againputs "Hi, I am #{name}"endbob = HelloWorld.new("Bob")
bob.greet #=> Hi, I am Bob
bob.greet_again #=> Hi, I am Bob
调用方法时,如果省略了接收者,Ruby就会默认把self作为该方法的接收者。因此,即使省略了self,也还是可以调用name方法但是有一点需要注意:
在调用像name=方法这样的以=结束的方法时,即使实例方法中已经有了name="Ruby"这样的定义,但如果仅在方法内部定义名为name的局部变量,也不能以缺省接收者的方式调用name=方法。这种情况下,我们需要用self.name="Ruby"的形式来显式调用name方法。备注 虽然self本身与局部变量形式相同,但由于它是引用对象本身时的保留字,因此即使对它进行赋值,也不会对其本身的值有任何影响。像这样,已经被系统使用且不能被我们自定义的变量名还有nil、true、false、__FILE__、__LINE__、__ENCODING__等。
如何在已经定义的类中追加新的类方法?
方法的接收者就是类本身(类对象)的方法称为类方法。第一种:
class << 类名 ~ end 这个特殊的类定义中,以定义实例方法的形式来定义类方法。class << HelloWorlddef hello(name)puts "#{name} said hello"end
endHelloWorld.hello("Ruby")第二种:
在class语句中使用self时,引用的对象是该类本身,因此,我们可以使用class << self ~ end 这样的形式,在class语句中定义类方法。这种方式很常见class HelloWorldclass << selfdef greet(name)puts "#{name} said Hello"endendendHelloWorld.greet("Perl")第三种:
我们还可以使用 def 类名.方法名 ~ end 这样的形式来定义类方法。
class HelloWorlddef self.speak(name)puts "#{name} said hello"end
endHelloWorld.speak("Matz")备注 class << 类名 ~ end 这种写法的类定义称为单例类定义,单例类定义中定义的方法称为单例方法。常量
常量就是指拥有固定值,且值一旦被定义就无法更改的变量,通常用大写字母来命名。
对于在类中定义的常量,我们可以像下面这样使用::通过类名来实现外部访问。类变量
以@@开头的变量称为类变量。类变量是该类所有实例的共享变量,这一点与常量类似,不同的是我们可以多次修改类变量的值。另外,与实例变量一样,从类的外部访问类变量时也需要存取器。不过,由于attr_accessor等存取器都不能使用,因此需要直接定义。class HelloCount@@count = 0def self.hello@@count += 1puts "hello #{@@count}"enddef self.counts@@countend
endHelloCount.hello
HelloCount.hello
HelloCount.hello
p HelloCount.counts
限制方法的调用
到目前为止,我们定义的方法都能作为实例方法被任意调用,但是有时候我们可能并不希望这样。例如,只是为了汇总多个方法的共同处理而定义的方法,一般不会公开给外部使用。
Ruby提供了3种方法的访问级别,我们可以按照需要来灵活调整。
- public…以实例方法的形式向外部公开该方法
- private…在指定接收者的情况下不能调用该方法(只能使用默认接收者的形式调用)
- protected…在同一个类中时可将该方法作为实例方法调用
public 和 private 的例子class AccTestdef pubputs "pub is a public meth!"endpublic :pubdef privputs "priv is a private meth"endprivate :privendacc = AccTest.new
acc.pub #=> 正常调用
acc.priv #=> 报错希望统一定义多个方法的访问级别时,可以使用下面的语法。
class AccTestpublicdef pubputs "pub is a public meth!"endprivatedef privputs "priv is a private meth"end
end没有指定访问级别的放马默认为public,但initialize方法是列外,它通常被定义为private。定义为protected的方法,在同一个类(及其子类)中可作为实例方法使用,而在除此以外的地方则无法使用。
示例:
class Pointattr_accessor :x, :yprotected :x=, :y=def initialize(x = 0.0, y = 0.0)@x, @y = x, yenddef swap(other)tmp_x, tmp_y = @x, @yself.x, self.y = other.x, other.yother.x, other.y = tmp_x, tmp_yreturn selfend
endobj = Point.new
obj2 = Point.new(30, 40)obj.swap(obj2)
p [obj.x, obj.y]
p [obj2.x, obj2.y]
p obj2.x=20 #=> 错误❌,因为方法不是public方法,无法在类外部调用
给原有的类添加方法
Ruby允许我们在已经定义好的类中添加方法。
class Stringdef count_strary = self.split(/\s+/)return ary.sizeend
endstr = "Just another ruby newbie"
p str.count_str #=> 4
继承
class 类名 < 父类名
类定义
end示例:重新定义[]运算符
其中super来调用父类中的[]方法
class RingArray < Arraydef [](i)idx = i % self.sizesuper (idx)end
endobj = RingArray[1, 2, 3, 4]
p obj[4]定义类时没有指定父类的情况下,Ruby会默认该父类为Object类。
Object类提供了很多便于实际编程的方法,但在某些情况下希望使用更加轻量的类,
而这时就可以使用BasicObject类。
BasicObject 类只提供了作为Ruby对象的最低限度的方法。类对象调用 instance_methods 方法后,就会以符号的形式返回该类的实例方法列表。下面我们就用这个方法来对比一下Object类和BasicObject类的实例方法。
定义BasicObject的子类时,与Object类不同,需要明确指定BasicObject类为父类。
alias与undef
有时我们会希望给已经存在的方法设置别名,这种情况下就需要使用alias方法。alias方法的参数为方法名或者符号名。alias 别名 原名 # 直接使用方法名
alias :别名 :原名 # 使用符号名class C1def helloputs "hello"end
endclass C2 < C1alias old_hello hellodef helloputs "hello again"end
endclass C2 < C1alias :old_hello :hellodef helloputs "hello again"end
endC2.new.hello
C2.new.old_helloundef 方法名 # 直接使用方法名
undef :方法名 # 使用符号名例如,在子类中希望删除父类定义的方法时,可以使用undef。
单例类
在下面的例子中,我们分别将"Ruby"赋值给str1对象和str2对象,然后只对str1对象添加hello方法。这样一来,两个对象分别调用hello方法时,str1对象可以正常调用,但str2对象调用时程序就会发生错误。class << strdef helloputs self.sizeend
endstr2 = ""
str.hello #=> 5
str2.hello #=> No method error到目前为止,我们已经多次只在特定的某个类中添加类方法。Ruby中所有的类都是Class类的对象,因此,Class类的实例方法以及类对象中所添加的单例方法都是类方法。
模块
模块是Ruby的特色功能之一。如果说类表现的是事物的实体(数据)及其行为(处理),那么模块表现的就只是事物的行为部分。模块与类有以下两点不同。
- 模块不能拥有实例
- 模块无法被继承、
Mixin就是将模块混合到类中。在定义类时使用include,模块中的方法、常量就都能被类使用。
- 虽然两个类拥有相似的功能,但是不希望把它们作为相同的种类(Class)来考虑
- Ruby不支持父类的多重继承,因此无法对已经继承的类添加共通的功能
module MyMoudledef helloputs "hello I am Moudle"end
endclass MyClass1include MyMoudle
endclass MyClass2include MyMoudle
endMyClass1.new.hello
MyClass2.new.hello模块可以提供独立的命名空间
所谓命名空间(namespace),就是对方法、常量、类等名称进行区分及管理的单位。由于模块提供各自独立的命名空间,因此A模块中的foo方法与B模块中的foo方法就会被程序认为是两个不同的方法。同样,A模块中的FOO常量与B模块的FOO常量也是两个不同的常量。
例如,在FileTest模块中存在与获取文件信息相关的方法。我们使用“模块名.方法名”的形式来调用在模块中定义的方法,这样的方法称为模块函数。p FileTest.exist?('case.rb')
p FileTest.size("case.rb")
p Math::PI
p Math.sqrt(2)如果没有定义与模块内的方法、常量等同名的名称,那么引用时就可以省略模块名。通过include可以把模块内的方法名、常量名合并到当前的命名空间。下面是刚才提到的Math模块的例子。我们使用module语句来创建模块。语法与创建类时几乎相同,模块名的首字母必须大写。
module 模块名 模块定义
endmodule HelloModuleVersion = "1.0"def hello(name)puts "Hello, #{name}."endmodule_function :hello
endp HelloModule::Version
HelloModule.hello("Ruby")
HelloModule.__send__(:hello, "Perl")
备注:如果只是定义了方法,但是并没有通过 module_function symbol 方法将模块函数公开给外部使用的话,无法通过模块名.方法名的形式调用。 一旦通过module_function symbol 公开方法以后,就能以这种方法调用方法。以“模块名.方法名”的形式调用时,如果在方法中调用self(接收者),就会获得该模块的对象。
def myselfselfend
p HelloModule.myself #=> HelloModule 模块名Mix-in
Note: 关于Mix-in
当我们想要知道一个类的继承关系时可以使用xxx.ancestors 来获取
p Regexp.ancestors #=> [Regexp, Object, Kernel, BasicObject]当我们想要知道一个类的父类可以用 Regexp.superclass方法来获取假设有个类C, 类C的实例在调用方法时,Ruby会按类C、模块M、类C的父类Object这个顺序查找该方法,并执行第一个找到的方法。被包含的模块的作用就类似于虚拟的父类。
虽然Ruby采用的是不允许具有多个父类的单一继承模型,但是通过利用Mixin,就既可以保持单一继承的关系,又可以同时让多个类共享功能。
单一继承的优点就是简单,不会因为过多的继承而导致类之间的关系变得复杂。但是另一方面,有时我们又会希望更加积极地重用已有的类,或者把多个类的特性合并为更高级的类,在那样的情况下,灵活使用单一继承和Mixin,既能使类结构简单易懂,又能灵活地应对各种需求。查找方法的规则
1.同继承关系一样,原类中已经定义了同名的方法时,优先使用该方法。
2.在同一个类中包含多个模块时,优先使用最后一个包含的模块。
3.嵌套include时,查找顺序也是线性的.
4.相同的模块被包含两次以上时,第二次以后的会被省略。extend方法
extend方法可以使单例类包含模块,并把模块的功能扩展到对象中。
module Adef nameputs "I am A"end
endstr = "Hello"
str.extend(A)
str.nameclass VendV.extend(A)
V.name
extend既可以用来扩展实例方法,也可以扩展类方法。见上面的示例。module Adef nameputs "I am A"end
endmodule Bdef nameputs "I am B"end
endmodule Cinclude Bdef nameputs "I am C"end
endclass TestModuleinclude Cinclude A
endp TestModule.ancestors #=> [TestModule, A, C, B, Object, Kernel, BasicObject]class TestModuleinclude C, A
endp TestModule.ancestors #=> [TestModule, C, B, A, Object, Kernel, BasicObject]
注意这里导入模块时,第一种方式是分行,第二种是用逗号隔开,先后顺序有区别,参考结果。
逗号之前的模块会被优先查找,而分行形式,最后倒入的模块会被优先查找。如果类 Mix-in 了模块,就相当于为该类添加了实例方法,这种情况下self代表的就是被mix-in的类的对象。即使是相同的方法,在不同的上下文调用时,其含义也会不一样,因此对于Mixin的模块,我们要注意根据实际情况判断是否使用模块函数功能。一般不建议在定义为模块函数的方法中使用self。除了之前介绍的定义类方法的语法外,使用 extend 方法也同样能为类对象追加类方法。下面是使用 extend 方法追加类方法,并使用 include 方法追加实例方法的一个例子。module ClassMethoddef cmethod"class method"end
endmodule InstanceMethodsdef imethod"instance method"end
endclass MyClassextend ClassMethodinclude InstanceMethods
endp MyClass.cmethod
p MyClass.new.imethod在这里,extend 与 include的区别在于 extend 用于扩展类方法,而include是扩展实例方法,这个不能搞错。
其实不管是类也好,实例也好,本质上都是对象,因为对于Class类来说,String等类,都是它的实例,因此类本质上也是对象,因此在调用方法时,只是为了区分接收者的对象类型,才有类方法和实例方法之区别。
Ruby中的类和模块和变量使用说明相关推荐
- Python中的类、模块和包究竟是什么?
Python培训教程:Python中的类.模块和包究竟是什么?在Python和其他编程语言中,都有类似或相同的概念,如对象.类.模块.包,名称都是一样的,只不过会有细微的一些区别,正是因为有这些存在, ...
- Ruby中的类与对象(一)
一些约定: 类名首字母大写 方法名首字母小写 end在Ruby中无处不在. 声明类 class BookInStock end 创建类对象 a_book = BookInStock.new anoth ...
- 在python中同一个类下不同方法之间变量的调用
栗子:(方法a中的b变量被方法c调用) class A: def a(self): self.b = 1 def c(self): # 要self.被调用方法的方法名 self.a() d = 2+s ...
- ruby 变量类中范围_Ruby中的类
ruby 变量类中范围 Ruby类 (Ruby Classes) In the actual world, we have many objects which belong to the same ...
- 04/28/2010 类,对象,变量
表示歌曲的Ruby类,歌曲(名字,演唱者,时长) 在Ruby程序中,initialize是一个特殊的方法. 当调用Song.new创建一个新的Song对象时,Ruby首先分配一些内存来保存未初始化的对 ...
- 如何初始化类的static成员变量?
如何初始化类的static成员变量? 类的static成员变量不被某一个对象所独有,而是被所有同类型的对象所共有. 只能在头文件中声明类的static成员变量,不可在头文件中初始化,否则会造成重定义. ...
- python中的类的属性是什么_python中类属性和数据属性的解释
python中的类叫class object,类的实例叫instance object. 类 Class Objects 类拥有两种操作,1.类属性 attribute references 2.实例 ...
- python 如何查看模块所有方法-Python查看模块(变量、函数、类)方法
前面章节中,详细介绍了模块和包的创建和使用(严格来说,包本质上也是模块),有些读者可能有这样的疑问,即正确导入模块或者包之后,怎么知道该模块中具体包含哪些成员(变量.函数或者类)呢? 查看已导入模块( ...
- python 调用另一个py_跟我自学python 第9章 类(9.4.6 在一个模块中导入另一个模块)...
大家好今天学习9.4.6 在一个模块中导入另一个模块 .今天我们继续学习python.马上类就学完了. 喜欢python就关注冠山巡山使. 我将跟大家一块自学完python语言. 9.4.6 在一个模 ...
- python一个函数调用另一个函数中的变量_在Python 中同一个类两个函数间变量的调用方法...
在Python 中同一个类两个函数间变量的调用方法 更新时间:2019年01月31日 15:44:54 作者:wxy_summer 今天小编就为大家分享一篇在Python 中同一个类两个函数间变量的调 ...
最新文章
- 参数等效模型可以用于_等效媒质理论(介电参数反演)
- inline-block 间距
- 第二十三周微职位elk日志系统
- java rmi 还有用吗_java rmi使用后的感想
- Vue报错Module not found: Error: Can‘t resolve ‘less-loader‘
- MySQL卸干净与下载安装(2020最新版+亲测踩坑可用)
- 关于验证码的DEMO
- java应用程序无法启动,Apache Tomcat / 6.0.32 - Web应用程序无法启动
- python编程( 第一份Windows平台运行的python代码)
- 有个码龄10年的程序员跟我说:“他编程从来不用鼠标”,我说:
- 终极人机大战要来了?AI打星际2即将直播,DeepMind暴雪发出神秘预告
- L1-080 乘法口诀数列 (20 分)-PAT 团体程序设计天梯赛 GPLT
- IntentService用法小结
- 多功能时钟电路的设计框图_OLED显示屏,行驱动电路设计,单片机AT89C51与和显示屏的硬件接线...
- 分享5款提高工作效率的CAD插件,总有一款适合你
- MAX485芯片收发详解 实现485通信
- 【位操作笔记】位合并 通过掩码
- 农村信用社改革试点专项借款管理办法(银发[2003]181号)
- RGB 转换至 YCbCr (YUV) 的计算公式
- 2019牛客暑期多校训练营(第八场) CDMA
热门文章
- 代码随想录第四天 leetcode 24、19、206
- node scripts/install.js 安装失败解决办法
- linux uvc 拍照程序,Linux UVC driver
- 【有奖征询】可查询商票及企业境外债软件有奖征询
- 英语的计算机软件如何拼写,软的英文单词
- 符号常量和常变量的区别
- Docker 基础之Dockerfile命令详解
- oracle ndb,NDB语法 - ivaneeo's blog - BlogJava
- “三权分立”模型之角色模型
- java workflow 教程_建议收藏,从零开始创建一个Activiti工作流,手把手教你完成...