I was playing around with class variables, and I knew that I could override a class variable in a subclass but I didn't realize where I call a class method matters. For example;
class Vehicle
##wheels = 4
def self.wheels
##wheels
end
end
puts Vehicle.wheels #4
puts Vehicle.wheels #4
puts Vehicle.wheels #4
class Motorcycle < Vehicle
##wheels = 2
end
The puts statement outputs 4, that all makes sense to me because I figured that you ##wheels is not overridden until an object is instantiated from the Motorcycle class. So then I did this:
class Vehicle
##wheels = 4
def self.wheels
##wheels
end
end
class Motorcycle < Vehicle
##wheels = 2
end
puts Vehicle.wheels #2
puts Vehicle.wheels #2
puts Vehicle.wheels #2
Now once I move those three lines to after the class Motorcycle they output 2... even though a Motorcycle object hasn't been instantiated. This confuses me. In detail what is happening here? Is it because class variables are loaded when we try to access it? How is this possible?
First of all, instantiation has nothing to do with it; this is a class variable, not an instance variable. Your code never instantiates anything; nor, as written, does it need to. We're just talking about some classes. But classes are first-class objects in Ruby, so that's fine.
Second, order does matter, because in Ruby all statements are executable. When you say
class Motorcycle < Vehicle
##wheels = 2
end
...all of that code executes right now, at the moment you say it — the moment when it is encountered as Ruby walks down the page obeying instructions. That code is not a mere template to be obeyed at some future time; it says: "create a Motorcycle class and set its class variable wheels to 2, now."
Third, you're not "overriding" anything. In the class Motorcycle, ##wheels is the very same class variable ##wheels that you defined for Vehicle earlier. It is not some other variable that "overrides" it. This is a single value that works like a kind of namespaced global, reachable from the class, any subclasses, and any instances thereof.
For more information, see (e.g.):
How do I override a variable in a Ruby subclass without affecting the superclass?
Ruby class instance variable vs. class variable
https://www.ruby-lang.org/en/documentation/faq/8/
If you want something you can override, what you're looking for is probably more like an instance variable in a class context, often called a "class instance variable" — e.g. like this:
class Vehicle
#wheels = 4
class << self
attr_reader :wheels
end
end
class Motorcycle < Vehicle
#wheels = 2
end
puts Vehicle.wheels # 4
puts Motorcycle.wheels # 2
All occurances of ##wheels refer to the exactly same variable. First you set it to 4, then you set it to 2. Why does it surprise you, that it has the value 2? Use #wheels instead (as matt) suggested, if you want to have a separate variable for both.
Having said this (and this is the only reason I'm writing this answer, because matt has already covered everything else in his answer), your usage of this "variable" suggests that is is supposed to be an invariable property of the respective class. That is, every motorcycle is supposed to have 2 wheels. In this case, it would perhaps make more sense to make it a constant of its class:
class Motorcycle < Vehicle
WHEELS = 2
end
class Unicycle < Vehicle
WHEELS = 1
end
and use it as
puts Motorcycle::WHEELS
If you have a variable v, where you only know that it is some subclasss of Vehicle, you can write
puts v.class::WHEELS
Whether you also define WHEELS in the class Vehicle itself, its a design question. If you intend to treat Vehicle as abstract class, it does not make sense to define a fixed number of wheels in it. For orthogonality, you could catch this case by doing a
class Vehicle
WHEELS = nil
end
if you want to, or just don't define it and rely on the fact that your code will raise an exception, if you try to access the wheels of something which is "just a plain vehicle".
If you do want to instantiate a Vehicle, of course it may make sense to set the constants there to, i.e., 4.
However, in this case I would consider allowing individual instances having a different number of wheels. The Reliant Robin was a car with 3 wheels, and trucks have sometimes 6 or 8 wheels. For maximum generality, I would therefore make the number of wheels an instance variable, and of course you can provide a default value:
class Vehicle
attr_reader :wheels
def initialize(wheels=4)
#wheels=wheels
end
end
class Motorcycle < Vehicle
attr_reader :wheels
def initialize(wheels=2)
super(wheels)
end
end
m = Motorcycle.new
puts m.wheels
Related
I see that with this code:
# filename: play.rb
class A
attr_reader :a
def initialize(num)
#a=num # I have to use # here
end
def m1
p "---"
p #a
p a
end
end
obj = A.new(1)
obj.m1
I get the output
$ ruby play.rb
"---"
1
1
As you see I can refer to a as either #a or a in the m1 method and both work.
Which should I use when and why?
In this case you don't use a local variable a but a getter method that gives you #a because that's have attr_reader :a. It generates a method #a() used as an getter.
What you really do is:
def m1
p "---"
p #a
p a()
end
If I have the accessor, I use it, not the instance variable. It lets me change the getter later.
I find this an interesting question, one that I had not given much thought to before today. I consulted the Ruby Style Guide, expecting it would provide some good advice, but it is curiously mute on the subject.
I make four suggestions below. These concern setters as well as getters. Others may disagree and have good reasons for doing so. Let's discuss! I look forward to reading comments.
In reading my remarks it may be helpful to put yourself in the place of someone reading code they wrote some time ago or someone reading someone else's code for the first time.
1. Getters used exclusively within a class should be private
I expect this is my least debatable suggestion, as I can see only disadvantages to making any method public when there is no reason to do so.
2. Methods named after instance variables should not have side effects
By this I mean if there is an instance variable #quantity, a method named :quantity should do no more than return the value of #quantity (i.e., be a getter) and a method named :quantity= should do no more than assign a value to #quantity (i.e., be a setter). That is, such methods should not have side effects.
Suppose, for example, a ("pseudo-") setter were defined to assign a value to an instance variable after accounting for a 10% spoilage factor:
def quantity=(q)
0.9 * q
end
If the reader of the code were to miss this definition, but knew there was an instance variable #quantity, the natural assumption would be that :quantity= was a setter, which might mask errors or waste time in testing/debugging. Better, I think, would be to write:
class C
attr_writer :quantity
def initialize
end
#...
def adj_quantity_for_spoilage
self.quantity *= 0.9
end
#...
end
c = C.new
c.quantity = 100
c.adj_quantity_for_spoilage
3. Do not use setters within a class
For example, I suggest writing:
class C
attr_accessor :quantity
def change(x)
#quantity = x
end
end
rather than:
class C
attr_accessor :quantity
def change(x)
self.quantity = x
end
end
The main reason is that it is all-to-easy to inadvertently omit self. in self.quantity = x, in which case x would be incorrectly and silently assigned to a newly-created local variable quantity.
There is a secondary reason in the case where there is no need to access the setter from outside that class. As with getters, we would want setters to be private in this situation, but that is not possible since they must have the explicit receiver self. Recall that private methods, by definition, do not have explicit receivers.
Lastly, I see no argument for using self.quantity over #quantity, particularly in view of the fact that the former requires the keying of four additional characters.1
4. Do not use getters within a class
I expect this to be my most controversial suggestion and I will be the first to admit that if one rejects it the earth will no doubt continue to orbit the sun. Moreover, I concede that using getters in this way does save the typing of one character, the hard-to-reach-without-looking "#".
When I read:
x = m(#quantity)
I know immediately that #quantity is an instance variable, regardless of my familiarity with the code. If I've forgotten (or never knew) what the variable contains or how it is used, the path to my elucidation is clear. True, if I know there is an instance variable #quantity, the above has no advantage over:
x = m(quantity)
If, however, I don't know if there is an instance variable #quantity, I would be faced with three possibilities: quantity is a local variable, a getter or a method that is not a getter. The time required to complete my investigation should not take much longer than if I were tracking down #quantity, but those seconds do add up.
Let's consider another thing: what are the consequences of misspelling the name of an instance variable versus misspelling its getter (something I do frequently):
x = m(#qauntity)
versus:
x = m(qauntity)
#qauntity will return nil, which may lead to an exception being raised, but possibly not soon or not at all.
qauntity will almost certainly raise an "no method or variable" exception immediately, giving it the edge in this situation.
In sum, I am suggesting that it generally is best to use getters and setters outside of class definitions only, and that they have no side effects.
Your thoughts?
1. Over a lifetime of coding, typing those four extra characters could amount to hours of time wasted that could otherwise be used productively (e.g., playing pong).
I need to make the following code functional by building a "Car" class. I feel I must be overlooking something simple. any help would be appreciated. The # indicates the expected output
# Make the following code functional by building a Car class
c = Car.new("blue")
puts c.color # blue
puts c.repaint_count # 0
c.paint("red")
c.paint("green")
puts c.repaint_count # 2
puts c.color # green
here is what I have done:
class Car
##repaint_count = 0
def initialize(color)
#color = color
end
def self.paint(color)
#color = color
##repaint_color += 1
end
def self.color
#color
end
end
I guess I am being thrown by the c.color / c.paint: should I be defining these methods and setting them equal to class or something else ? I think I am missing something about classes and inheritance.
I guess I am being thrown by the c.color / c.paint: should I be
defining these methods and setting them equal to class or something
else ? I think I am missing something about classes and inheritance.
I think in fact you are over-complicating it by worrying about these things at this stage. The question is not about inheritance. Although in some ways it is poorly specified in that it is possible to mis-interpret the question text and assign some properties to the class other than the instance.
So first things, you have got that the question expects you to implement a Car class, and that there is internal state to track for the current color and the number of times it has changed. You have partly mis-understood the repaint count and made it a class variable. It needs to be an instance variable - it is intended to be the number of times a specific car has been re-painted, not the number of times any car has been re-painted. Although the example numbers would be the same, the difference is that the question asks for c.repaint_count not Car.repaint_count, and c is an instance of Car, hence you want to store the count as an instance variable - set it to 0 in the constructor.
Similar confusion in your accessor code. Ruby's use of self is a little confusing - it changes meaning on context in the code. If you changed your def self.paint to just def paint and similarly for color then with the change from last paragraph, you are pretty much done.
One last thing, you need to implement repaint_count accessor similar to how you have done with color (and again, without the self. which would make it a class method)
You seem to be confusing classes and instances. c is an instance of Car, and is not the class Car itself. Unless you want to count the total repaint_count throughout the Car class, you should not use a class variable ##repaint_count, but should use an instance variable. Your paint method is a class method, and is not well defined. In addition the definition body looks like you randomly put something.
class Car
attr_reader :color, :repaint_count
def initialize color
#color = color
#repaint_count = 0
end
def paint color
#color = color
#repaint_count += 1
end
end
Well, this looks like a homework question, which I'm not going to write for you. However I'll give you some pointers.
You create/ open up a class like this.
class Foo
end
When you open up a class like this you can set it up to accept arguments immediately, like so:
class Foo
attr_accessor :bar, :bar_counter
def initialize(arg_1)
#bar = arg_1
#bar_counter = 0
end
# And add methods with any name like so.
def increase_bar
#bar_counter += 1
end
def change_bar(arg)
#bar = arg
end
end
This will explain the differences between attr_accessor, attr_reader, attr_writer https://stackoverflow.com/a/4371458/2167965
People have various opinions on Codecademy, but in my opinion it's perfect for teaching basic syntax like this. There's also Ruby Koans, and Ruby Test First.
My recommendations would be to start with codecademy to learn the syntax, and move to Test First to flesh out those concepts.
I've had a chance to look around in StackOverflow and found this same question which I was trying to better understand from Ruby Koans (Ruby Koans: explicit scoping on a class definition part 2).
class MyAnimals
LEGS = 2
class Bird < Animal
def legs_in_bird
LEGS
end
end
end
def test_who_wins_with_both_nested_and_inherited_constants
assert_equal 2, MyAnimals::Bird.new.legs_in_bird
end
# QUESTION: Which has precedence: The constant in the lexical scope,
# or the constant from the inheritance heirarachy?
# ------------------------------------------------------------------
class MyAnimals::Oyster < Animal
def legs_in_oyster
LEGS
end
end
def test_who_wins_with_explicit_scoping_on_class_definition
assert_equal 4, MyAnimals::Oyster.new.legs_in_oyster
end
# QUESTION: Now Which has precedence: The constant in the lexical
# scope, or the constant from the inheritance heirarachy? Why is it
# different than the previous answer?
Based on the explanation in the link, it seems like the main confusion others (including myself) had was because of the class definition:
class MyAnimals::Oyster < Animal
# stuff goes in here
end
My original thought was that MyAnimals::Oyster means that the Oyster class was defined within MyAnimals. In other words, I thought the above code was analogous to the following code:
class MyAnimals
class Oyster < Animal
# stuff goes in here
end
end
To test my thought, I did the following in IRB:
class MyAnimals
LEGS = 2
class Bird < Animal
def legs_in_bird
LEGS
end
end
end
class MyAnimals::Oyster # You should notice that I'm not inheriting from Animal anymore
def legs_in_oyster
LEGS
end
end
If my reasoning is correct, then I would expect that the below code returns 2
MyAnimals::Oyster.new.legs_in_oyster # => NameError: uninitialized constant MyAnimals::Oyster::LEGS
Since this doesn't return 2, can someone explain to me why it doesn't return 2?
EDIT:
I neglected to add the Animal class; here it is:
class Animal
LEGS = 4
def legs_in_animal
LEGS
end
class NestedAnimal
def legs_in_nested_animal
LEGS
end
end
end
Go back to the order in which Ruby searches for the value. Step 1 is 'the enclosing scope'. When you define a nested class like this
class Animal
class Bird
end
end
'Bird' is enclosed in the 'Animal' scope.
When you do it like this
class Animal
end
class Animal::Bird
end
Although you've defined 'Bird' as nested within 'Animal', at the time you defined 'Bird', the enclosing scope was the global scope. Go back to IRB and define LEGS = 0 (in the global scope) and try your Oyster again.
In strictly lexically/statically scoped languages like C++ or Java, identifiers are resolved by simply checking the current scope, ascending one scope higher and repeating until the base most scope is reached. For example: if your example were C++, LEGS would be searched for first in the calling class Bird/Oyster, then Animal, then My Animal.
Ruby has a kind of dynamic scoping. Each object, even if it resides in the same 'place' can have its own scope lookup order depending on how it was defined or create at runtime. You can think of each method as having a stack of scopes to search, and how it was defined pushes new scopes onto that stack.
Because of the way Bird is defined it gets (BaseScope is not its real name, you did not provide enough code to provide this) BaseScope::MyAnimals::Bird, BaseScope::MyAnimals::Animal, BaseScope::MyAnimals and BaseScope to search through for resolving names.
While The first Oyster only gets BaseScope::MyAnimals::Oyster, BaseScope::MyAnimals::Animal and BaseScope.
The Oyster without inheritance gets even less, just BaseScope::MyAnimals::Oyster and BaseScope.
Each use of the class keyword and inheritance in this example pushes another scope to check onto the stack of scopes for its contents to search. So using class MyAnimals::Oyster only pushed one entry onto this stack.
Edit
For simplicity I left out the method legs_in_oyster. It is a scope that could be searched. It's trivial definition is self explanatory and including it would add much useless text to this answer.
I also left out the global scope for simplicity. I know Koans has at least one scope at or between BaseScope and the global scope.
I'm trying to understand inheritance:
class Car
##wheels = 4
def wheel
##wheels
end
end
class StretchLimo < Car
##wheels = 6
def whee
##wheels
end
def turn_on_television
end
end
I instantiate some objects like so:
moe = Car.new
larry = StretchLimo.new
When I do moe.wheel, I get 6, when I'm expecting 4.
The Ruby tutorial I'm following says it's supposed to be 4. Larry.whee should obviously return 6.
By the way, the "wheel" and "whee" functions I added so I could see the values. Can anyone explain what's wrong here?
Class variables in Ruby are strange and confusing.
An idiomatic way to implement what you want is this:
class Car
def wheels
4
end
end
class StretchLimo < Car
def wheels
6
end
end
Car.new.wheels #=> 4
StretchLimo.new.wheels #=> 6
Whats happening is that class variables are shared between all instances of a class. Because StrechLimo is a subclass of Car instances of StrechLimo also see this variable.
## is a class variable so it is shared across all objects instantiated from a given class and all derived class. Because Ruby is interpreted, until you instantiate a StretchLimo object, it should not look at any of the StretchLimo code, so if you did the following:
moe = Car.new
moe.wheel # should give 4
larry = StretchLimo.new
moe.wheel # should give 6
Because when the StretchLimo gets interpreted it updates the ##wheels class variable to be 6. On the other hand, if you declared "wheels" with only one "#" (#wheels), it would be an instance variable specific to the object itself, and you would get your preferred behavior.
I've come across the following example from this tutorial:
class Song
##plays = 0
def initialize(name, artist, duration)
#name = name
#artist = artist
#duration = duration
#plays = 0
end
def play
#plays += 1
##plays += 1
"This song: ##plays plays. Total ###plays plays."
end
end
s1 = Song.new("Song1", "Artist1", 234) # test songs
s2 = Song.new("Song2", "Artist2", 345)
puts s1.play
puts s2.play
puts s1.play
puts s1.play
Is ##plays politely accessible only inside the class Song? This commentary brings up the point of not recommending the use of class variables. Is it b/c they are often not required in common-day use, and create a lot of debugging headaches when used?
Class variables are never really required. But the reason isn't that they're shared state. I mean, it's good to avoid shared state where you can, but that's not the real problem here.
The reason they're recommended against is, as shown in that article, they are really confusing. In particular, a class's class variables are shared by its subclasses and instances of its subclasses. For example:
class Parent
end
class Child1 < Parent
##class_var = "Child1's"
end
class Child2 < Parent
##class_var = "Child2's"
end
With this code, Child1 and its instances will all see a class variable named ##class_var with the value "Child1's" and Child2 and its instances will all see a class variable named ##class_var with the value "Child2's". But suppose later on we reopen Parent and write this:
class Parent
##class_var = "Parent's"
end
Now Parent and the instances it creates will all see a class variable named ##class_var with the value "Parent's". But that's not all. Now that the parent class has this variable, Child1 and Child2 suddenly share the variable, so all of the ##class_vars have the value "Parent's". And if you reassign the variable in Child1, it's still shared, so all of the classes get updated. How confusing!
Instead of class variables, you can just use instance variables of the class, like this:
class Parent
#class_var = "Parent's"
def self.class_var
#class_var
end
end
class Child1 < Parent
#class_var = "Child1's"
end
class Child2 < Parent
#class_var = "Child2's"
end
Now, Parent.class_var will return "Parent's", Child1.class_var will return "Child1's" and Child2.class_var will return "Child2's" — just like you expect.
A class variable is a variable that is shared among all instances of a class. This means only one variable value exists for all objects instantiated from this class. This means that if one object instance changes the value of the variable, that new value will essentially change for all other object instances. Another way of thinking of thinking of class variables is as global variables within the context of a single class.
##plays #is a class variable
#plays #is an instance variable
$plays #is a global variable accessed outside a class
So in your example you created a class variable ##plays to calculate the total number of songs played for all songs. Since it is a class variable, it cannot be accessed outside the class alone. If you wanted to access the total number of plays you can use a global variable. They start with a dollar sign $plays (in your case). I warn you, you should stay away from using global variables as they are problematic for numerous reasons. One thing you may consider is to create a method that pushes all song instances into an array. You can then sum all plays across all songs through iterators. Way more secure, way less prone to programmer error.
Edit:
Here are why global variables are bad
Are global variables bad?
The ## variable will a class variable. This is generally bad practice. In your code its redundant because #plays == ##plays (unless you set ##plays elsewhere in your code (bad practice))
Actually now that I look at it, they aren't really the same. #plays keeps a count of how many times an individual song has been played, and ##plays will keep a count of all songs. Still, its likely bad practice to use ##plays. Usually, you'd have a parent class like "Player" that is managing all the songs. There should be an instance variable called #total_plays in the "Player" class.