Python 3 - call a method to change another method in a class - methods

Trying to make a class where i can make a dog stop and come
And I'm struggling with what to write in my status method and also how to make the status call's output change when you call the comeHere() and stop() method
class Dog:
def setYear(self, i):
if len(i) == 4:
self.__year = i
def getYear(self):
return self.__year
def status(self):
def comeHere(self):
def stop(self):
def main():
watson = Dog()
watson.setYear(2011)
print("The dog is born in ",watson.getYear())
print(watson.status())
print("trying to make the dog come")
watson.comehere()
print("Come here boy... ",watson.status())
print("Trying to make him stop again")
watson.stop()
print("Stop! ... ",watson.status())
main()
Want the output to be like this
The dog is born in 2011
The dog is standing
Trying to make the dog to come
Come here boy... the dog is coming
Trying to make him stop again
Stop! ... The dog is standing
How do I call the status method and make the output change depended on the call before status()

Use status as variable not function
class Dog:
def __init__(self):
self.status="The dog is standing"
def setYear(self, i):
if len(str(i)) == 4:
self.__year = i
def getYear(self):
return self.__year
def comeHere(self):
self.status="the dog is coming"
def stop(self):
self.status="The dog is standing"
watson = Dog()
watson.setYear(2011)
print("The dog is born in ",watson.getYear())
print(watson.status)
print("trying to make the dog come")
watson.comeHere()
print("Come here boy... ",watson.status)
print("Trying to make him stop again")
watson.stop()
print("Stop! ... ",watson.status)
output
The dog is born in 2011
The dog is standing
trying to make the dog come
Come here boy... the dog is coming
Trying to make him stop again
Stop! ... The dog is standing

Maybe you could make status an variable in Dog class and modify it when one of the functions is called.
class Dog:
def __init__(self):
self.status = None
def setYear(self, i):
if len(i) == 4:
self.__year = i
def comeHere(self):
self.status = "Dog is coming"
watson = Dog()
watson.comeHere()
print(watson.status) # returns 'Dog is coming"

Related

Class variables and inheritance. Subclass specific variables?

I am struggling a bit with subclassing and class variables. I was expecting the class variables set by a class method call of the subclass to be specific to that subclass, but I see them being set in the super class.
For example, if I run the following code:
class Obj
##can_say = []
def self.says word
##can_say.push(word)
end
def self.listens_to calling
define_method calling do
"I say #{##can_say}"
end
end
end
class Dog < Obj
says "woof"
says "bark bark"
listens_to "goodboy"
end
class Cat < Obj
says "meaow"
says "purr"
listens_to "lord"
end
cat = Cat.new
dog = Dog.new
puts("Dog: #{dog.goodboy}")
puts("Cat: #{cat.lord}")
I get:
ruby/test$ ruby test.rb
Dog: I say ["woof", "bark bark", "meaow", "purr"]
Cat: I say ["woof", "bark bark", "meaow", "purr"]
I was expecting:
ruby/test$ ruby test.rb
Dog: I say ["woof", "bark bark"]
Cat: I say ["meaow", "purr"]
Is there a way to achieve that?
I kind of found a way:
class Obj
class << self
attr_accessor :can_say
end
def self.says word
if #can_say.nil?
#can_say = Array.new
end
#can_say.push(word)
end
def self.listens_to calling
define_method calling do
"I say #{self.class.can_say}"
end
end
end
class Dog < Obj
says "woof"
says "bark bark"
listens_to "goodboy"
end
class Cat < Obj
says "meaow"
says "purr"
listens_to "lord"
end
cat = Cat.new
dog = Dog.new
puts("Dog: #{dog.goodboy}")
puts("Cat: #{cat.lord}")
Now if I can somehow get rid of:
if #can_say.nil?
#can_say = Array.new
end

Getting object name in ruby

class Animal
def initialize(noise)
#noise = noise
end
def say
puts "#{self.class} does #{#noise}"
end
end
dog = Animal.new("Woof Woof")
cat = Animal.new("Meow Meow")
dog.say
cat.say
How can I print dog does woof woof instead of Animal does Woof Woof?
To be clear, Animal is the name of the class you have created. That is why when you
puts " #{self.class} etc... "
the text Animal appears.
Now in your example, cat and dog are the names of variables. These are nothing more than references to the objects you create in the lines:
dog = Animal.new("Woof Woof")
cat = Animal.new("Meow Meow")
The variable may be called dog or cat, but the class remains Animal. To my knowledge, there is no good way to get name of a variable, short of some really convoluted introspection tools.
On the other hand: If you simply want your animals to have names, consider adding a name attribute.
class Animal
def initialize(name, noise)
#name = name
#noise = noise
end
def say
puts "#{#name} does #{#noise}"
end
end
dog = Animal.new("Dog", "Woof Woof")
cat = Animal.new("Cat", "Meow Meow")
dog.say
cat.say
This is a code misunderstanding, no big deal. Animal IS the object, that's why it will always print Animal does Woof Woof. You know that Animal is the object because you've declared it: class Animal, a class is an object.
dog is an instance of that Animal class. If you want to print the instance name, then you need to actually create an attribute, just like you've done with #noise. So, one example might be:
def initialize(noise, name)
#noise = noise
#name = name
end
dog = Animal.new("Woof Woof", "dog")
Note you'll probably want to not pass params individually but in a params hash, so it becomes clearer to read.

how to get child class variable back into parent class in ruby

I would like to create a base class to handle all the methods. How would I achieve this using ruby in this scenario?
class Dog
def initialize
#breed = "good breed"
end
def show_dog
puts "#{self.class.first_name} of breed #{#breed}"
end
end
class Lab < Dog
attr_reader :first_name
def initialize
#first_name = "good dog"
end
end
lb = Lab.new()
lb.show_dog
The expected result would be "good dog of breed good breed"
Thank you in advance :)
self.class.first_name doesn't do what you probably wanted to do. You need to use #first_name directly.
You need to call the parent class' constructor from the child class; it's not called automatically.
This works:
class Dog
def initialize
#breed = "good breed"
end
def show_dog
puts "#{#first_name} of breed #{#breed}" ### <- Changed
end
end
class Lab < Dog
attr_reader :first_name
def initialize
super ### <- Added
#first_name = "good dog"
end
end
lb = Lab.new()
lb.show_dog
self.class.first_name means Lab.first_name, not Lab's instance lb.first_name

Have a parent class's method access the subclass's constants

For example:
class Animal
def make_noise
print NOISE
end
end
class Dog < Animal
NOISE = "bark"
end
d = Dog.new
d.make_noise # I want this to print "bark"
How do I accomplish the above? Currently it says
uninitialized constant Animal::NOISE
I think that you don't really want a constant; I think that you want an instance variable on the class:
class Animal
#noise = "whaargarble"
class << self
attr_accessor :noise
end
def make_noise
puts self.class.noise
end
end
class Dog < Animal
#noise = "bark"
end
a = Animal.new
d = Dog.new
a.make_noise #=> "whaargarble"
d.make_noise #=> "bark"
Dog.noise = "WOOF"
d.make_noise #=> "WOOF"
a.make_noise #=> "whaargarble"
However, if you are sure that you want a constant:
class Animal
def make_noise
puts self.class::NOISE
# or self.class.const_get(:NOISE)
end
end
one way to do it without class instance variables:
class Animal
def make_noise
print self.class::NOISE
end
end
class Dog < Animal
NOISE = "bark"
end
d = Dog.new
d.make_noise # prints bark
I think you have the wrong concept here. Classes in Ruby are similar to classes in Java, Smalltalk, C#, ... and all are templates for their instances. So the class defines the structure and the behavior if its instances, and the parts of the structure and behavior of the instances of its subclasses but not vice versae.
So direct access from a superclass to a constant in a subclass is not possible at all, and that is a good thing. See below how to fix it. For your classes defined, the following things are true:
class Animal defines the method make_noise.
instances of class Animal may call the method make_noise.
class Dogdefines the constant NOISE with its value.
instances of Dog and the class Dog itself may use the constant NOISE.
What is not possible:
Instances of Animal or the class Animal itself have access to constants of the class Dog.
You may fix that by the following change:
class Animal
def make_noise
print Dog::NOISE
end
end
But this is bad style, because now, your superclass (which is an abstraction about Dog and other animals) knows now something that belongs to Dog.
A better solution would be:
Define an abstract method in class Animal which defines that make_noise should be defined. See the answer https://stackoverflow.com/a/6792499/41540.
Define in your concrete classes the method again, but now with the reference to the constant.
If you're doing this to configure your sub classes so that the base class has access to the constants then you can create a DSL for them like this:
module KlassConfig
def attr_config(attribute)
define_singleton_method(attribute) do |*args|
method_name = "config_#{attribute}"
define_singleton_method method_name do
args.first
end
define_method method_name do
args.first
end
end
end
end
class Animal
extend KlassConfig
attr_config :noise
def make_noise
puts config_noise
end
end
class Dog < Animal
noise 'bark'
end
This way is a bit more performant as on each method call you don't have to introspect the class to reach back (or is it forward?) for the constant.
If you want it the Object-Oriented Way (TM), then I guess you want:
class Animal
# abstract animals cannot make a noise
end
class Dog < Animal
def make_noise
print "bark"
end
end
class Cat < Animal
def make_noise
print "meow"
end
end
d = Dog.new
d.make_noise # prints bark
c = Cat.new
c.make_noise # prints meow
If you want to refactor to prevent duplicating the code for print:
class Animal
def make_noise
print noise
end
end
class Dog < Animal
def noise
"bark"
end
end
class Cat < Animal
def noise
if friendly
"meow"
else
"hiss"
end
end
end
d = Dog.new
d.make_noise # prints bark
c = Cat.new
c.make_noise # prints meow or hiss

create class dynamically, with inheritance and particular accessors

I'd like to build a function "create" that gives me the following capabilities :
zoo=[]
zoo << create(:dog,4)
zoo[0].class #Dog class
myDog=zoo[0].new("foobar") #instance of Dog
myDog.legs #4 legs because my dog is a Dog
zoo[0].class.legs #4
zoo[0].class.superclass #Animal
zoo[0].class.superclass.legs #whatever, but they have legs
"create(:dog,4)" produces a new class Dog that inherits Animal etc
Can you help about this apparently simple metaprogramming question ?
class Animal
def self.legs=(legs)
#legs = legs
end
def self.legs
#legs
end
def legs
self.class.legs
end
end
def create(sym, legs)
klass = Object.const_set(sym.to_s.capitalize, Class.new(Animal))
klass.legs = legs
klass
end
kdog = create(:dog, 4)
kalien = create(:alien, 3)
dog = kdog.new
alien = kalien.new
puts kdog
puts kdog.class
puts kdog.superclass
puts kdog.legs
puts dog.class
puts dog.legs
puts "------"
puts kalien
puts kalien.class
puts kalien.superclass
puts kalien.legs
puts alien.class
puts alien.legs
Output:
Dog
Class
Animal
4
Dog
4
------
Alien
Class
Animal
3
Alien
3

Resources