• 《Rubu基础教程第五版》第八章笔记 类和模块


    类是什么

    生成一个对象,可以用类.new,通过.class来查看他原来属于的类,通过instance_of?来查看实例是否属于这个类。跟Python中的type与.__class__差不多

    >> arr = Array.new
    => []
    >> p arr
    []
    => []
    >> str = 'hello'
    => "hello"
    >> arr.class
    => Array
    >> str.class
    => String
    >> arr.instance_of?(Array)
    => true
    >> str.instance_of?(String)
    => true
    >> str.instance_of?(Object)
    => false
    >> 
    

    继承

    子类,父类跟Python差不多,Ruby不支持多继承

    BasicObject类是Ruby中所有类的父类,它定义了作为Ruby对象的最基本的功能

    BasicObject类是最最基础的类,甚至连一般对象需要的功能都没有定义。因此普通对象所需要的类一般都被定义为obejct类。字符串,数组等都是object类的子类。

    >> String.is_a?(Object)
    => true
    >> str
    => "hello"
    >> str.is_a?(String)
    => true
    >> str.is_a?(Object)
    => true
    >> 
    

     is_a?这个方法跟Python中的isinstance函数功能差不多,返回的都是boll值

    创建类

    Hello, world. I am Bob.
    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat hello_class.rb 
    class HelloWorld
      def initialize(myname = "Ruby")
        @name = myname
      end
    
      def hello
        puts "Hello, world. I am #{@name}."
      end
    end
    
    bob = HelloWorld.new("Bob")
    alice = HelloWorld.new("Alice")
    ruby = HelloWorld.new
    
    bob.hello  
    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ 
    

     整个定义跟Python差不多

    initialize方法等于Python中的__init__,@相当于Python中self

    实例变量与实例方法

    跟Python一样,不解释了。

    存取器

    在ruby中,从对象外部不能直接访问实例变量或对实例变量赋值,需要通过方法来访问对象的内部

    class HelloWorld
      def initialize(myname = "Ruby")
        @name = myname
      end
    
      def hello
        puts "Hello, world. I am #{@name}."
      end
    
      def name=(value)      # 这个是赋值的方法
        @name = value
      end
    end
    

    这样修改实例变量比较麻烦,可以定义一些相应的存储器

    attr_reander :name   只读(定义name方法)

    attr_writer :name    只写(定义name=方法)

    attr_accessor :name   读写 (定义以上两个方法)

    特殊变量 self

    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat hello_class_self.rb 
    class HelloWorld
      attr_accessor :name
    
      def greet
        puts "Hi, I am #{self.name}"    # 可以添加使用的属性,self可以省略
      end
      
      def test_name
        self.name = "Ruby"     # 添加方法给调用者的属性赋值,不要添加self
      end
    
    end
    
    bob = HelloWorld.new
    alice = HelloWorld.new
    ruby = HelloWorld.new
    
    bob.name = "new_bob"
    bob.test_name
    bob.greet 
    

    类方法

    方法的接收这是类本身的方法称为类方法。

    第一种, 用class << 类名的方式来创建类方法

    ruby2.6不能实现该方法的定义

    第二种, 在类中用 class << self ~ end的形式,在class里面定义类方法

    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat class_method_1.rb 
    class HelloWorld
      class << self    ! 定义类方法的标记
        def hello(name)
          puts "#{name} said hello."
        end
      end
    end
    
    
    HelloWorld.hello "Join"
    

     还有在方法前面添加类名.或者self.的方式添加类方法

    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat class_method_2.rb 
    class HelloWorld
      def HelloWorld.hello(name)
        puts "#{name} said hello."
      end
    end
    
    
    HelloWorld.hello "Join"
    
    class HelloWorld
      def self.hello(name)    # 通过self定义类方法
        puts "#{name} said hello."
      end
    end
    
    
    HelloWorld.hello "Join"
    

     从示例来看用self来定义类方法最方便。

    类常量,可以直接通过::外部访问到

    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat class_constant.rb 
    class HelloWorld
      Version = "1.0"
    end
    
    p HelloWorld::Version    # 通过::读取类的常量的定义
    

    类变量

    cat: hello_c: No such file or directory
    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat hello_count.rb 
    class HelloCount
      @@count = 0
    
      attr_accessor :name
      def self.count    # 定义类方法
        @@count
      end
    
      def initialize(myname="Ruby")
        @name = myname
      end
    
      def hello
        @@count += 1
        puts "Hello, world. Iam #{name}.
    "
      end
    end
    
    bob = HelloCount.new("Bob")
    alice = HelloCount.new("Alice")
    ruby = HelloCount.new
    
    p HelloCount.count
    bob.hello
    alice.hello
    ruby.hello
    

     类变量,能够实例的方法进行改变

    限制方法的调用

    自己有个转帖有着更加详细的介绍:https://www.cnblogs.com/sidianok/p/12982555.html

    书中的代码感觉不是很好,抄写一遍再说

    public 默认的默认的方法都是公开的

    private 私有的,在指定接受者的情况下不能调用该方法

    protected 在同一个类中可将该方法作为实例方法调用的

    测试代码

    class AccTest
      def pub
        puts "pub is a public method."
      end
      
      public :pub   # 把pub方法定义为公开的,默认就是
      
      def priv
        puts "priv is a private method."
      end
      
      private :priv
    
      def run_priv
        priv
      end
    
    end
    
    acc = AccTest.new
    acc.pub
    acc.run_priv
    acc.priv
    

    希望定义多个方法的访问级别,可以使用下面的语法

    class AccTest
      public   # 这里下面都是公开的
    
      def pub
        puts "pub is a public method."
      end
      
      def run_priv
        priv
      end
      
      private    # 这里下面的都是私有的
    
      def priv
        puts "priv is a private method."
      end
    
    end
    
    acc = AccTest.new
    acc.pub
    acc.run_priv
    acc.priv
    

    定义protected的方法,在同一类(及其子类)中可作为实例方法使用,而在除此以外的地方无法使用

    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat point.rb 
    class Point
      attr_accessor :x, :y  # 定义存储器,可以对该属性进行读取,赋值
      protected :x=, :y=   # 定义属性赋值x=,y=设定为protected
    
      def initialize(x=0.0,y=0.0)
        @x, @y = x, y
      end
    
      def swap(other)
        tem_x, tem_y = @x, @y
        @x, @y = other.x, other.y
        other.x, other.y = tem_x, tem_y  # 在同一个类中调用protected方法
        self
      end
    end
    
    p0 = Point.new
    p1 = Point.new(1.0, 2.0)
    
    p [p0.x, p0.y]
    p [p1.x, p1.y]
    
    p0.swap(p1)
    
    
    p [p0.x, p0.y]
    p [p1.x, p1.y]
    
    p0.x = 10.0
    

     仔细看了以下书中的示例代码,起始写的还是很不错的

    扩展类

    在原有类的基础上添加方法

    class String
      def count_word
        ary = self.split(/s+/) # 用正则,匹配切割空格字符
        ary.size
      end
    end
    
    
    str = "Just Another Ruby Newbie"
    p str.count_word
    

     不需要继承什么的,直接在原来类的方式上面,添加方法就可以

    继承

    class 类名<父类名

      类定义

    end

    class RingArray < Array
      def [](i)
        idx = i % size
        super(idx)      # 调用父类的方法
      end
    end
    
    
    wday = RingArray["星期日","星期一","星期二","星期三","星期四","星期五","星期六"]
    p wday[6]
    p wday[11]
    p wday[15]
    p wday[-1]
    

    类对象调用instance_methods方法后,就会以符号的形式返回该类的实例方法列表

    >> Object.instance_methods
    => [:instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_get, :instance_variable_set, :instance_variables, :singleton_method, :method, :public_send, :define_singleton_method, :public_method, :extend, :to_enum, :enum_for, :<=>, :===, :=~, :!~, :eql?, :respond_to?, :freeze, :inspect, :object_id, :send, :to_s, :display, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :equal?, :!, :__id__, :==, :instance_exec, :!=, :instance_eval, :__send__]
    >> BasicObject.instance_methods
    => [:equal?, :!, :__id__, :==, :instance_exec, :!=, :instance_eval, :__send__]
    >> 
    

     上面两个不通的类,实例以后返回的方法列表

    alias与undef

    alias 别名 原名

    alias :别名 :原名

    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat alias_sample.rb 
    class C1
      def hello
        "Hello"
      end
    end
    
    class C2 < C1
      alias :old_hello :hello   # 这里用old_hello hello这样直接用方法名的方式也可以操作
      
      def hello
        "#{:old_hello}, again"
      end
    end
    
    obj = C2.new
    p obj.old_hello
    p obj.hello
    

    undef

    在子类中删除父类的方法

    可以通过 undef 方法名或者:方法名  的方式删除父类定义的方法

    单例类

    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat single_class.rb 
    str1 = "Ruby"
    str2 = "Ruby"
    
    class << str1
      def hello
        "Hello. #{self}!"
      end
    end
    
    p str1.hello
    

     这个到可以运行,但没发现有什么用

    模块是什么

    模块是表示事务的行为部分,动作部分

    模块不能被实例,模块不能被继承

    模块的使用

    Minx_in就是将模块混合到类中。在定义时使用include,模块中的方法、常量就都能被类使用。

    代码案例

    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat mixin_sample.rb 
    module MyModule
      # 共同的方法等
    end
    
    class MyClass1
      include MyModule
      # MyClass1独有的方法
    end
    
    class MyClass2
      include MyModule
      # MyClass2独有的方法
    end
    

    提供命名空间

    所谓命名空间(namespace),就是对方法、常数、类等名称进行区分及管理的单位

    不通的模块就是一个不同的命令空间

    通过Math模块来示例

    >> p Math::PI       # 调用模块的参数
    3.141592653589793
    => 3.141592653589793
    >> p Math::sqrt(2)    # 调用模块的方法
    1.4142135623730951
    => 1.4142135623730951
    >> p Math.sqrt(2)
    1.4142135623730951
    => 1.4142135623730951
    >> 
    >> include Math    # 命名空间导入当前的命名空间
    => Object
    >> PI
    => 3.141592653589793
    >> sqrt(5)
    => 2.23606797749979
    >> 
    

    ::很强大,可以调用模块类的常量,还可以调用模块方法

    创建模块

    module HelloModule
      Version = "1.0"
    
      def hello(name)
        puts "Hello, #{name}"
      end
    
      module_function :hello     # 指定hello方法为模块方法
    
    end
    
    p HelloModule::Version
    p HelloModule.hello("Ruby")
    
    include HelloModule
    
    p Version
    p hello "Python"
    

    常量

    与类一样,通过::可以取出模块或者类中的常量

    方法的定义

    定义了模块内的方法,只能在模块内部使用,或者包含此模块的语句中使用,不能直接用模块名.方法名的方式使用。

    如果想直接通过模块名.方法名的方式调用方法,需要通过module_function :xxx 的方式来定义

    Mix_in

    module M
      def meth
        "meth"
      end
    
      # module_function :meth # 一旦定义称为了模块方法,不能被实例调用了
    end
    
    class C include M
    end
    
    c = C.new
    # p M.meth
    p c.meth
    

    如果像知道一个类里面是否包含某个模块可以用类名.include?(模块名)

    我们可以用过ancestors[祖先的意思]返回继承关系的列表  类名.ancestors

    superclass返回自身的父类    类名.superclass

    查找方法的规则

    1、元类中已经定义了同名的方法时,有限使用该方法

    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat find_methods_1.rb 
    module M
      def meth
        "M#meth"
      end
    end
    
    class C include M
    
      def meth
        "C#meth"
      end
    end
    
    c = C.new
    p c.meth
    

    2、在同一个类中包含多个模块时,优先使用最后一个包含的模块

    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat find_methods_2.rb 
    module M1
    end
    
    module M2
    end
    
    class C
      include M1
      include M2    # 优先继承这个的
    end
    
    p C.ancestors
    

    3、嵌套include时,查找顺序也是线性的

    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat find_methods_3.rb 
    module M1
    end
    
    module M2
    end
    
    module M3 include M2
    end
    
    class C
      include M1
      include M3    # 优先继承这个的
    end
    
    p C.ancestors  #  [C, M3, M2, M1, Object, Kernel, BasicObject]
    

    相同的模块被包含两次,第二次以后会被省略

    module M1
    end
    
    module M2
    end
    
    class C
      include M1
      include M2
      include M1   # 这个会被忽略
    end
    
    p C.ancestors
    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ 
    

    extend方法

    extend方法可以使用单例类包含模块,并把模块的功能扩张到对象中

    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat test_extend.rb 
    module Edition
      def edition(n)
        "#{self} 第#{n}版"
      end
    end
    
    str = "Ruby基础教程"
    
    str.extend(Edition)   # 将模块Mix-in进对象
    
    p str.edition 5
    

    类与Mix-in

    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat test_include_extend.rb 
    module ClassMethods
      def cmethod
        "class method"
      end
    end
    
    module InstanceMethods
      def imethod
        "instance method"
      end
    end
    
    class MyClass
      include InstanceMethods
      extend ClassMethods
    end
    
    p MyClass.cmethod
    p MyClass.new.imethod
    

    这里用到了include和extend继承模块的方法与类方法

    面向对象的程序设计

    面向对象的语言中的"对象"就是指数据(或者说数据的几个)以及操作该数据的方法的组合

    封装(encapsulation),就是指使对象管理的数据不能直接从外部进行操作,数据的更新、查寻等操作都必须调用对象的方法来完成。

    这个做的比Python好太多了,通过学习ruby让我对类的三大特性有了更好的了解。

    多态就使同名的方法属于多个对象(不同对象的处理结果不一样)这种现象,这就使多态。

    鸭子类型

    鸭子类型的说法来至于"能像鸭子一样走路,能像鸭子一样叫,那一定使鸭子"这句话

    这句话的意思,对象的特征并不是由其种类(类极其启程关系决定的),而是由对象具有什么样的行为(拥有什么方法)决定的

    用示例代码来演示

    shijianzhongdeMacBook-Pro:chapter_8 shijianzhong$ cat fetch_anddowncase.rb 
    def fetch_and_downcase(ary, index)
      if str = ary[index]
        str.downcase     # 装换成小写
      end
    end
    
    
    ary = ["Bob", "Foo", "Woo"]
    p fetch_and_downcase(ary, 1)
    
    hash = {0 => "Bob", 1 => "Foo", 2 => "Woo"}
    p fetch_and_downcase(hash, 1)
    

     执行的输出结果是一样的

    函数只关心arr[index]形式获取元素

    获取的元素能执行downcase方法

    并不关心传递进来的是什么元素,这个就是鸭子类型,如果报错,可以通过摘取错误的方式,要不然通过instance来处理,可能会错过很多合适的鸭子。

    感谢ruby让我对鸭子类型有了形象的理解。

  • 相关阅读:
    Python学习(四)数据结构 —— list tuple range
    Python学习(四)数据结构 —— bool
    Python学习(四)数据结构 —— int float
    Python学习(四)数据结构(概要)
    Python学习(三)流程控制
    Python学习(二)Python 简介
    Python学习(一)安装、环境配置及IDE推荐
    iOS崩溃日志 如何看
    python 读取excel表格内不同类型的数据
    python xlrd读取excel常用方法
  • 原文地址:https://www.cnblogs.com/sidianok/p/12990078.html
Copyright © 2020-2023  润新知