我是ruby和编程的新手,我正在尝试掌握一些关键概念。 鉴于我有一类狗,具有以下特点。
class Dog
attr_accessor :type, :popularity, :total
def initialize(type = nil)
@type = type
end
def total_dogs
Dog.count
end
def total
Dog.where(:type => self.type).size
end
def popularity
total.to_f/total_dogs
end
end
我想要了解的是,ruby如何通过getter / setter方法将属性持久化到实例。我清楚地知道,如果我实例化一个新实例然后将属性保存到该实例,那么这些属性就与该实例相关联,因为如果我查看该对象,则属性如下所示:
@dog = Dog.new
@dog
=> #<Dog:0x007fa8689ea238 @type=nil>
我很容易理解,当我传递@dog对象时,总是将@type属性作为nil。 但是,我理解的情况是我将这个@dog对象传递给另一个类。就像我做的那样:
Owner.new(@dog)
当我在所有者类中并且我打电话给@ dog.popularity时,它是如何知道该实例的受欢迎程度的?在运行时,所有方法都被处理,然后该实例总是与当时的值绑定?如果这没有任何意义或我离开,请道歉。
答案 0 :(得分:6)
当你这样做时
@dog = Dog.new
你做了两件特别的事情
1)为代码当前所在的任何对象创建一个实例变量@dog
2)实例化Dog的新实例(包含其所有方法和属性)并将其引用分配给@dog
@dog是一个变量,恰好指向你在那时创建的Dog实例(“类的实例”,通常意思是“对象”)。您可以将其他变量设置为指向同一个实例,而在Ruby中,这通常是您传递数据的方式。对象包含实例变量,这些实例变量指向更多对象。
使用赋值运算符(即“=”)可以将变量指向任何其他对象。
依次回答你的问题:
当我在所有者班级时,我打电话给@ dog.popularity是怎么回事 知道该实例的受欢迎程度值吗?
您必须小心使用Ruby(以及一般的OO语言)来区分描述和问题中的类和对象。 Ruby我假设您指的是Owner类中的一行代码,并且您打算使用所有者对象。我还假设@dog是您添加到所有者的属性。
在这种情况下,Ruby知道因为@dog指向您添加到所有者的Dog对象。每个Dog对象都有自己的所有Dog实例变量的副本。你确实需要在Ruby中注意,因为变量指向到对象,你不是简单地将同一个Dog对象传递给所有的所有者(即它们都有效地共享一只狗)。因此,您需要了解何时创建新实例(通过新实例)以及何时只是处理现有引用。
在运行时处理所有方法,然后处理该实例 总是与当时的价值挂钩?
没有。在运行时,基本的Ruby只会执行您编码的赋值。在分配了分配它们的代码之前,实例变量甚至可能不存在。如果你使用attr_reader等方法,那么变量至少会存在(但除非你在初始化期间分配一些东西,否则它们将是nil)
答案 1 :(得分:2)
Niel对此有很好的答案,我只是想补充一点。
数狗:)
你需要一个类变量才能做到这一点..
class Dog
@@count = 0 # this is a class variable; all objects created by this class share it
def initialize
@@count += 1 # when we create a new Dog, we increment the count
end
def total
@@count
end
end
还有另一种方法可以使用“Class对象的实例变量”,但这是一个高级主题。
访问实例变量
在Ruby中,变量实际上只是对对象/实例的引用。
> x = 1
=> 1
> x.class
=> Fixnum
> 1.instance_variables
=> []
x是对象'1'的引用,它是类Fixnum的一个实例。 '1'对象是Fixnum的一个实例,它不包含任何实例变量。 它与引用新的“Dog”实例没有任何不同。
同样,您可以说x = Dog.new
,然后x是对Dog类实例的引用。
class Dog
attr_accessor :legs # this defines the 'legs' and 'legs=' methods!
end
x = Dog.new
x.instance_variables
=> [] # if you would assign legs=4 during "initialize", then it would show up here
x.legs = 4 # this is really a method call(!) to the 'legs' method
x.instance_variables # get created when they are first assigned a value
=> [:legs]
如果将这样的引用传递给方法调用,或者只传递给另一个类或者只是自己评估它也没关系 - Ruby知道它是一个对象引用,并查看对象内部以及它的继承链如何解决问题
解析方法名称
这只是部分事实:)当解释x.legs
时,Ruby检查对象的类继承链中是否有一个方法,它响应该名称'legs'。
它并没有神奇地访问具有相同名称的实例变量!
我们可以通过“attr_reader:legs”或“attr_accessor:legs”定义一个方法'leg',或者自己定义方法。
class Dog
def legs
4 # most dogs have 4 legs, we don't need a variable for that
end
end
x.legs # this is a method call! it is not directly accessing a :legs instance variable!
=> 4
x.instance_variables
=> [] # there is no instance variable with name ":legs"
如果我们尝试将其作为方法和实例变量实现,则会发生这种情况:))
class Dog
attr_accessor :legs # this creates "def legs" and "def legs=" methods behind the scenes
def legs # here we explicitly override the "def legs" method from the line above.
4
end
end
x = Dog.new
x.legs # that's the method call we implemented explicitly
=> 4
x.legs = 3 # we can still assign something to the instance_variable via legs=
=> 3
x.legs # the last definition of a method overrides previous definitions
# e.g. it overrides the automatically generated "legs" method
=> 4
attr_accessor :legs
只是这样做的简写:
class Dog
def legs
@legs
end
def legs=(value)
@legs = value
end
end
没有魔法方式实例变量被自动访问。它们总是通过一种方法访问,以后可以覆盖它。
我希望这对你有意义
答案 2 :(得分:0)
创建对象时,无需使用@
符号。变量是对象。所以,如果你有多只狗,你可以这样做:
myDog = Dog.new(brown)
yourDog = Dog.new(white)
从那里,你可以说:
yourDog.type #white
myDog.type #brown
你不会做的是:
@dog = Dog.new #myDog
@dog = Dog.new #yourDog
如果您需要一个对象的多个版本,您只需给它们不同的名称。因此,如果您创建多只狗并将它们传递给其他对象,它们将起作用。例如:
假设您的所有者类是:
Class Owner
def initialize(pet)
puts "my pet is #{pet.type}"
end
然后使用实例变量:
me = Owner.new(myDog) #my pet is brown
you = Owner.new(yourDog) #my pet is white
答案 3 :(得分:0)
“type”和“popular”都是“dog”实例上的方法。他们的定义如下:
class Dog
# getter
def type
@type
end
def popularity
total.to_f/total_dogs
end
end
这大致相当于:
class Dog
attr_accessor :type
def popularity
total.to_f/total_dogs
end
end
请注意,attr_accessor只是定义getter方法的快捷方式。如果你自己定义一个方法,那么使用attr_accessor是没有意义的:
class Dog
attr_accessor :popularity
# this will override getter defined by attr_accessor
def popularity
total.to_f/total_dogs
end
end
回到你的问题:@ dog.type在@dog上调用type方法,返回它的实例变量; @ dog.popularity在@dog上调用流行度方法,它在运行中进行计算(由你定义)并返回结果。这里没有魔法!