<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 <a href="https
Step1:  Intermediate Python - Objects
At this point in your Python journey, you should be familiar with the following concepts and when to use them.
different data types
string
number
list
tuple
dictionary
printing
for and while loops
if/else statements
functions
code commenting
In this lab, we will move into the more advanced concept of objects. You may have heard of object-oriented programming, especially in other languages. If not, don't worry. This will be a gentle introduction that will give you the skills you need to know in order to build your own objects in Python.
Objects
Introduction
It is likely that you have seen programs written in a procedural programming style. These programs consist of procedures (also called functions and methods) that operate on data passed to them.
Imagine that you had a function compute_paycheck that computed the weekly paycheck for a worker. If you wanted to compute the paycheck of a given employee in a procedural style, you would pass the necessary data to compute the pay to the compute_paycheck function.
employee_data = get_employee_data()
pay = compute_paycheck(employee_data)
Though you could write something like this in Python, it isn't necessarily idiomatic to the language. What this means is that Python tends to work better and look better when you use object-oriented programming.
Python is an object-oriented language. This means that your program can be modeled as logical objects with methods built in to the object to operate on data.
In an object-oriented programming style, you could encode each employee as its own object, and write a method called compute_paycheck which returns the weekly paycheck for a given employee. In that case, computing an employee's paycheck would look more like the following
Step2:  You can create your own object using the class keyword.
Step3:  Why did we use the keyword class and not object? You can think of the class as a template for the object, and the object itself as an instance of the class. To create an object from a class, you use parentheses to instantiate the class.
Step4:  Notice that Cow is a class and that elsie and annabelle are Cow objects. The text following at indicates where in memory these objects are stored. You might have to look closely, but elsie and annabelle are located at different locations in memory.
Adding methods to a class is easy. You simply create a function, but have it indented so that it is inside the class.
Step5:  You can then call the method directly on the class.
Step6:  While you can call talk() on the Cow class, you can't actually call talk() on any instances of Cow, such as elsie and annabelle.
In order to make Elsie and Annabelle talk, we need to pass the self keyword to the talk method. In general, all object functions should pass self as the first parameter.
Let's modify the Cow class to make talk an object (also known as instance) function instead of a class function.
Step7:  Now talk can be called on objects of type Cow, but not on the Cow class itself.
You can add as many methods as you want to a class.
Step8:  Initialization
There are special functions that you can define in a class. These functions do things like initialize an object, convert an object to a string, determine the length of an object, and more.
These special functions all start and end with double-underscores. The most common of these is __init__. __init__ initializes the class. Let's add an initializer to our Cow class.
Step9:  There are a few new concepts in the code above.
__init__
You can see that __init__ is passed the object itself, commonly referred to as self.
__init__ can also accept any number of other arguments. In this case, we want the name of the cow. We save that name in the object (represented by self), and also use it in the talk method.
__name
Notice that the instance variable __name has two underscores before it. This naming is a way to tell Python to hide the variable from the rest of the program, so that it is only accessible to other methods within the object. This data hiding provides encapsulation which is an important concept in object-oriented programming.
Had __name been called name or _name (single-underscore), it would not be hidden, and could then be accessed on the object (eg. annie.name).
There are many different double-underscore (dunder) methods. They are all documented in the official Python documentation.
Inheritance
Python objects are able to inherit functionality from other Python objects. Let's look at an example.
Step10:  In the code above, we create an Animal class that has a generic implementation of the talk and eat functions that we created earlier. We then create a Cow object that implements its own talk function but relies on the Animal's eat function. We also create a Worm class that fully relies on Animal to provide talk and eat functions.
The reason this is so useful is that we can scaffold classes to inherit base features. For example, we might want different base classes Plant and Animal that represent generic plants and animals respectively. Then, we could create different plants such as Cactus and Sunflower inheriting from the Plant class, and different animals such as Cow and Worm.
Python also supports multiple inheritance and many layers of inheritance. In the code below, move and eat are methods of the base class Animal, which are then inherited by different types of animals.
Step11:  Exercises
Exercise 1
In the code block below, create a Cow class that has an __init__ method that accepts a name and breed so that a cow can be created like
Step12:  Exercise 2
Take the Cow class that you implemented in exercise one, and add a double-underscore method so that if you create a cow using
Step13:  Exercise 3
Take the Cow class that you implemented in exercise two (or one), and add a double-underscore method so that print(repr(elsie)) prints
Step14:  Exercise 4
Fix the Car class in the code inheritance below so that "Vroom!" is printed.
Student Solution
<ASSISTANT_TASK:>
Python Code:
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Explanation: <a href="https://colab.research.google.com/github/google/applied-machine-learning-intensive/blob/master/content/00_prerequisites/01_intermediate_python/00-objects.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>
Copyright 2019 Google LLC.
End of explanation
for data in (
  1,                         # integer
  3.5,                       # float
  "Hello Python",            # string
  (1, "funny", "tuple"),     # tuple
  ["a", "list"],             # list
  {"and": "a", "dict": 2}    # dictionary
  ):
  print("Is {} an object? {}".format(type(data), isinstance(data, object)))
Explanation: Intermediate Python - Objects
At this point in your Python journey, you should be familiar with the following concepts and when to use them.
different data types
string
number
list
tuple
dictionary
printing
for and while loops
if/else statements
functions
code commenting
In this lab, we will move into the more advanced concept of objects. You may have heard of object-oriented programming, especially in other languages. If not, don't worry. This will be a gentle introduction that will give you the skills you need to know in order to build your own objects in Python.
Objects
Introduction
It is likely that you have seen programs written in a procedural programming style. These programs consist of procedures (also called functions and methods) that operate on data passed to them.
Imagine that you had a function compute_paycheck that computed the weekly paycheck for a worker. If you wanted to compute the paycheck of a given employee in a procedural style, you would pass the necessary data to compute the pay to the compute_paycheck function.
employee_data = get_employee_data()
pay = compute_paycheck(employee_data)
Though you could write something like this in Python, it isn't necessarily idiomatic to the language. What this means is that Python tends to work better and look better when you use object-oriented programming.
Python is an object-oriented language. This means that your program can be modeled as logical objects with methods built in to the object to operate on data.
In an object-oriented programming style, you could encode each employee as its own object, and write a method called compute_paycheck which returns the weekly paycheck for a given employee. In that case, computing an employee's paycheck would look more like the following:
employee_data = get_employee_data()
pay = employee_data.compute_paycheck()
In this case, compute_paycheck is a method that is bound to the returned argument employee_data, and can be called directly on this type.
A method is just a function that is tied to an object. However, the terms "function" and "method" are often used interchangeably. See here for a more in-depth discussion.
Using object-oriented programming does not mean that you can't pass data to functions/methods. Imagine that the employee data only contained information like hourly wage and tax holdouts. In this case, compute_paycheck would need to know the number of hours worked in order to calculate the employee's pay.
employee_data = get_employee_data()
hours_worked = get_hours_worked()
pay = employee_data.compute_paycheck(hours_worked)
In the example above, you can see the procedural and object-oriented styles mixed together in the same block. (The hours_worked variable is computed using the get_hours_worked function, and the employee_data variable is computed using the get_employee_data function.) However, even these variables could be computed in an object-oriented style. For example, hours_worked could come from an object representing the time clock, and employee_data could come from an object representing the HR system.
employee_data = hr.get_employee_data()
hours_worked = timeclock.get_hours_worked()
employee_data.compute_pay(hours_worked)
In Python, everything is an object. The code below uses the inbuilt isinstance function to check if each item is an instance of an object.
End of explanation
class Cow:
  pass
Explanation: You can create your own object using the class keyword.
End of explanation
# Create an instance of Cow called elsie
elsie = Cow()
# Create an instance of Cow called annabelle
annabelle = Cow()
print(Cow)
print(elsie)
print(annabelle)
Explanation: Why did we use the keyword class and not object? You can think of the class as a template for the object, and the object itself as an instance of the class. To create an object from a class, you use parentheses to instantiate the class.
End of explanation
class Cow:
  def talk():
    print("Moo")
Explanation: Notice that Cow is a class and that elsie and annabelle are Cow objects. The text following at indicates where in memory these objects are stored. You might have to look closely, but elsie and annabelle are located at different locations in memory.
Adding methods to a class is easy. You simply create a function, but have it indented so that it is inside the class.
End of explanation
Cow.talk()
Explanation: You can then call the method directly on the class.
End of explanation
class Cow:
  def talk(self):
    print("Moo")
elsie = Cow()
elsie.talk()
Explanation: While you can call talk() on the Cow class, you can't actually call talk() on any instances of Cow, such as elsie and annabelle.
In order to make Elsie and Annabelle talk, we need to pass the self keyword to the talk method. In general, all object functions should pass self as the first parameter.
Let's modify the Cow class to make talk an object (also known as instance) function instead of a class function.
End of explanation
class Cow:
  def talk(self):
    print("Moo")
    
  def eat(self):
    print("Crunch")
elsie = Cow()
elsie.eat()
elsie.talk()
Explanation: Now talk can be called on objects of type Cow, but not on the Cow class itself.
You can add as many methods as you want to a class.
End of explanation
class Cow:
  def __init__(self, name):
    self.__name = name
  
  def talk(self):
    print("{} says Moo".format(self.__name))
annie = Cow("Annabelle")
annie.talk()
elly = Cow("Elsie")
elly.talk()
Explanation: Initialization
There are special functions that you can define in a class. These functions do things like initialize an object, convert an object to a string, determine the length of an object, and more.
These special functions all start and end with double-underscores. The most common of these is __init__. __init__ initializes the class. Let's add an initializer to our Cow class.
End of explanation
class Animal:
  def talk(self):
    print("...")  # The sound of silence
  
  def eat(self):
    print("crunch")
class Cow(Animal):
  def talk(self):
    print("Moo")
class Worm(Animal):
  pass
cow = Cow()
worm = Worm()
cow.talk()
cow.eat()
worm.talk()
worm.eat()
Explanation: There are a few new concepts in the code above.
__init__
You can see that __init__ is passed the object itself, commonly referred to as self.
__init__ can also accept any number of other arguments. In this case, we want the name of the cow. We save that name in the object (represented by self), and also use it in the talk method.
__name
Notice that the instance variable __name has two underscores before it. This naming is a way to tell Python to hide the variable from the rest of the program, so that it is only accessible to other methods within the object. This data hiding provides encapsulation which is an important concept in object-oriented programming.
Had __name been called name or _name (single-underscore), it would not be hidden, and could then be accessed on the object (eg. annie.name).
There are many different double-underscore (dunder) methods. They are all documented in the official Python documentation.
Inheritance
Python objects are able to inherit functionality from other Python objects. Let's look at an example.
End of explanation
class Animal:
  def move(self):
    pass
  
  def eat(self):
    pass
class Legless(Animal):
  def move(self):
    print("Wriggle wriggle")
class Legged(Animal):
  def move(self):
    print("Trot trot trot")
class Toothless(Animal):
  def eat(self):
    print("Slurp")
class Toothed(Animal):
  def eat(self):
    print("Chomp")
class Worm(Legless, Toothless):
  pass
class Cow(Legged, Toothed):
  pass
class Rock:
  pass
def live(animal):
  if isinstance(animal, Animal):
    animal.move()
    animal.eat()
w = Worm()
c = Cow()
r = Rock()
print("The worm goes...")
live(w)
print("The cow goes...")
live(c)
print("The rock goes...")
live(r)
Explanation: In the code above, we create an Animal class that has a generic implementation of the talk and eat functions that we created earlier. We then create a Cow object that implements its own talk function but relies on the Animal's eat function. We also create a Worm class that fully relies on Animal to provide talk and eat functions.
The reason this is so useful is that we can scaffold classes to inherit base features. For example, we might want different base classes Plant and Animal that represent generic plants and animals respectively. Then, we could create different plants such as Cactus and Sunflower inheriting from the Plant class, and different animals such as Cow and Worm.
Python also supports multiple inheritance and many layers of inheritance. In the code below, move and eat are methods of the base class Animal, which are then inherited by different types of animals.
End of explanation
# Your code goes here
Explanation: Exercises
Exercise 1
In the code block below, create a Cow class that has an __init__ method that accepts a name and breed so that a cow can be created like:
elsie = Cow("Elsie", "Jersey")
Name the class variables name and breed.
Make sure that if the name and breed of cow passed to the constructor are changed, the values stored in the instance variables reflect the different names. In other words, don't hard-code "Elsie" and "Jersey".
Student Solution
End of explanation
# Your code goes here
Explanation: Exercise 2
Take the Cow class that you implemented in exercise one, and add a double-underscore method so that if you create a cow using:
cow = Cow("Elsie", "Shorthorn")
Calling print(cow) prints:
Elsie is a Shorthorn cow.
Hint: you may want to look through the Python documentation on special method names to find the dunder method that dictates a string representation of the object.
Student Solution
End of explanation
# Your code goes here
Explanation: Exercise 3
Take the Cow class that you implemented in exercise two (or one), and add a double-underscore method so that print(repr(elsie)) prints:
Cow("Elsie", "Jersey")
Student Solution
End of explanation
# Your code goes here
class Vehicle:
  def go():
    pass
class Car:
  def go():
    print("Vroom!")
# No changes below here!
car = Car()
if isinstance(car, Vehicle):
  car.go()
Explanation: Exercise 4
Fix the Car class in the code inheritance below so that "Vroom!" is printed.
Student Solution
End of explanation
<END_TASK>