Python Class

# We use the "class" operator to get a class
class Human:
 
    # A class attribute. It is shared by all instances of this class
    species = "H. sapiens"
 
    # Basic initializer, this is called when this class is instantiated.
    # Note that the double leading and trailing underscores denote objects
    # or attributes that are used by python but that live in user-controlled
    # namespaces. Methods(or objects or attributes) like: __init__, __str__,
    # __repr__ etc. are called magic methods (or sometimes called dunder methods)
    # You should not invent such names on your own.
    def __init__(self, name):
        # Assign the argument to the instance's name attribute
        self.name = name
 
        # Initialize property
        self.age = 0
 
    # An instance method. All methods take "self" as the first argument
    def say(self, msg):
        print ("{name}: {message}".format(name=self.name, message=msg))
 
    # Another instance method
    def sing(self):
        return 'yo... yo... microphone check... one two... one two...'
 
    # A class method is shared among all instances
    # They are called with the calling class as the first argument
    @classmethod
    def get_species(cls):
        return cls.species
 
    # A static method is called without a class or instance reference
    @staticmethod
    def grunt():
        return "*grunt*"
 
    # A property is just like a getter.
    # It turns the method age() into an read-only attribute
    # of the same name.
    @property
    def age(self):
        return self._age
 
    # This allows the property to be set
    @age.setter
    def age(self, age):
        self._age = age
 
    # This allows the property to be deleted
    @age.deleter
    def age(self):
        del self._age
 
 
# When a Python interpreter reads a source file it executes all its code.
# This __name__ check makes sure this code block is only executed when this
# module is the main program.
if __name__ == '__main__':
    # Instantiate a class
    i = Human(name="Ian")
    i.say("hi")                     # "Ian: hi"
    j = Human("Joel")
    j.say("hello")                  # "Joel: hello"
    # i and j are instances of type Human, or in other words: they are Human objects
 
    # Call our class method
    i.say(i.get_species())          # "Ian: H. sapiens"
    # Change the shared attribute
    Human.species = "H. neanderthalensis"
    i.say(i.get_species())          # => "Ian: H. neanderthalensis"
    j.say(j.get_species())          # => "Joel: H. neanderthalensis"
 
    # Call the static method
    print(Human.grunt())            # => "*grunt*"
    print(i.grunt())                # => "*grunt*"
 
    # Update the property for this instance
    i.age = 42
    # Get the property
    i.say(i.age)                    # => 42
    j.say(j.age)                    # => 0
    # Delete the property
    del i.age
    # i.age                         # => this would raise an AttributeError

Multiple Inheritance

# Another class definition
class Bat:
 
    species = 'Baty'
 
    def __init__(self, can_fly=True):
        self.fly = can_fly
 
    # This class also has a say method
    def say(self, msg):
        msg = '... ... ...'
        return msg
 
    # And its own method as well
    def sonar(self):
        return '))) ... ((('
 
if __name__ == '__main__':
    b = Bat()
    print(b.say('hello'))
    print(b.fly)
 
 
# from "filename-without-extension" import "function-or-class"
from human import Human
from bat import Bat
 
# Batman inherits from both Human and Bat
class Batman(Human, Bat):
 
    # Batman has its own value for the species class attribute
    species = 'Superhero'
 
    def __init__(self, *args, **kwargs):
        # Typically to inherit attributes you have to call super:
        #super(Batman, self).__init__(*args, **kwargs)      
        # However we are dealing with multiple inheritance here, and super()
        # only works with the next base class in the MRO list.
        # So instead we explicitly call __init__ for all ancestors.
        # The use of *args and **kwargs allows for a clean way to pass arguments,
        # with each parent "peeling a layer of the onion".
        Human.__init__(self, 'anonymous', *args, **kwargs)
        Bat.__init__(self, *args, can_fly=False, **kwargs)
        # override the value for the name attribute
        self.name = 'Sad Affleck'
 
    def sing(self):
        return 'nan nan nan nan nan batman!'
 
 
if __name__ == '__main__':
    sup = Batman()
 
    # Instance type checks
    if isinstance(sup, Human):
        print('I am human')
    if isinstance(sup, Bat):
        print('I am bat')
    if type(sup) is Batman:
        print('I am Batman')
 
    # Get the Method Resolution search Order used by both getattr() and super().
    # This attribute is dynamic and can be updated
    print(Batman.__mro__)       # => (<class '__main__.Batman'>, <class 'human.Human'>, <class 'bat.Bat'>, <class 'object'>)
 
    # Calls parent method but uses its own class attribute
    print(sup.get_species())    # => Superhero
 
    # Calls overloaded method
    print(sup.sing())           # => nan nan nan nan nan batman!
 
    # Calls method from Human, because inheritance order matters
    sup.say('I agree')          # => Sad Affleck: I agree
 
    # Call method that exists only in 2nd ancestor
    print(sup.sonar())          # => ))) ... (((
 
    # Inherited class attribute
    sup.age = 100
    print(sup.age)
 
    # Inherited attribute from 2nd ancestor whose default value was overridden.
    print('Can I fly? ' + str(sup.fly))

Leave a Reply

Your email address will not be published. Required fields are marked *