# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3 (system-wide)
#     language: python
#     metadata:
#       cocalc:
#         description: Python 3 programming language
#         priority: 100
#         url: https://www.python.org/
#     name: python3
#     resource_dir: /ext/jupyter/kernels/python3
# ---

# +
#Inheritance - A way to form new classes from other classes that already been defined
# Benifits - Reuse code already worked on
# -

#BASED CLASS
class Animal():
    def __init__(self):
        print('Animal Created')
        
    def who_am_i(self):
        print('I am an animal')
        
    def eat(self):
        print('I am eating')


my_animal = Animal()

my_animal.eat()


# inherit from Animal Class
class Dog(Animal):
    def __init__(self):# init for dog class
        Animal.__init__(self) # running init method from animal
        print('Dog Created')
    def who_am_i(self): # overwrite methods from parent class
        print('I am a dog')
    def bark(self): # add methods too
        print('Woof!')
    def eat(self):
        print('I am a dog eating')


my_dog = Dog()

# Dog class is able to use methods from Animal Class
my_dog.who_am_i()

my_dog.eat()


# +
#POLYMORPHISM
# Refers a way different object classes can share the same method name and those methods can be called from the same place even though a different variety of object might be passed in.

class Dog():
    def __init__(self,name):
        self.name = name
    def speak(self):
        return(f'{self.name} says woof!')


# -

class Cat():
    def __init__(self,name):
        self.name = name
    def speak(self):
        return(f'{self.name} says Meow!')


my_dog = Dog(name='Niko')
my_cat = Cat(name='Felix')

print(my_dog.speak())
print(my_cat.speak())

for pet in [my_dog,my_cat]:
    print(type(pet))
    print(pet.speak())


def pet_speak(pet):
    print(pet.speak())


pet_speak(my_dog)

pet_speak(my_cat)


# ABSTRACT CLASS - NEVER EXPECT TO CREATE AN INSTANCE OF THIS CLASS INSTEAD JUST DESIGNED TO SERVE AS A BASE CLASS
class Animal():
    def __init__(self,name):
        self.name = name
    def speak(self):
        raise NotImplementedError("Subclass must implement this abstract method")


my_animal = Animal('Fred')
my_animal.speak() # should get an error


# +
class Dog(Animal):
    def speak(self):
        return self.name + ' says WOOF!'
    
class Cat(Animal):
    def speak(self):
        return self.name + ' says Meow!'
            


# -

fido = Dog('Fido')
isis = Cat('Isis')

print(fido.speak())
print(isis.speak())



StarcoderdataJupyterScriptsDedupFiltered