Skip to main content

Object Oriented

A set of data structures and the methods to handle them are formed into an object, objects with the same behaviour are grouped into classes, internal details are hidden through encapsulation, specialisation and generalisation are achieved through inheritance, and dynamic assignment based on object types is achieved through polymorphism. generalization through inheritance, and dynamic assignment based on object type through polymorphism

Classes and objects

A class is a blueprint and template for an object, and an object is an instance of a class. This explanation is a bit like explaining a concept in terms of a concept, but from this statement we can at least see that classes are abstract concepts and objects are concrete things. In the world of object-oriented programming, everything is an object, objects have properties and behaviours, each object is unique, and objects must belong to a class (type). Once we have extracted the static characteristics (properties) and dynamic characteristics (behaviour) of a large set of objects that share common characteristics, we can define something called a "class".

Class definition

In Python, you can define a class using the ``class'' keyword, and then define methods in the class using the functions you've learned before, so that the dynamic characteristics of the object can be described, as shown in the code below.

class Student(object):

# __init__ is a special method used to initialise the object when it is created
# With this method we can bind the name and age attributes to the student object
def __init__(self, name, age):
self.name = name
self.age = age

def study(self, course_name):
print('%s is studying %s.' % (self.name, course_name))

# PEP 8 requires identifiers to have their names in all lower case with multiple words joined by underscores
# But some programmers and companies prefer to use hump nomenclature (humped identifiers)
def watch_movie(self):
if self.age < 18:
print('%s can only watch "Bears".' % self.name)
else:
print('%s is watching an island romance movie.' % self.name)

def main():
# Create a student object and specify a name and age
stu1 = Student('Luo Hao', 38)
# Send a stuy message to the object
stu1.study('Python Programming')
# Send a watch_av message to the object
stu1.watch_movie()
stu2 = Student('Wang Sledgehammer', 15)
stu2.study('ideology and character')
stu2.watch_movie()


if __name__ == '__main__':
main()

Access visibility issues

Because in many object-oriented programming languages, we usually set an object's properties to be private (private) or protected (protected), which simply means that no outside access is allowed, while an object's methods are usually public (public), because a public method is a message that the object can accept. In Python, there are only two types of access to properties and methods, public and private. If you want a property to be private, you can use two underscores at the beginning when naming the property, and the following code verifies this.

class Test:

def __init__(self, foo):
self.__foo = foo

def __bar(self):
print(self.__foo)
print('__bar')


def main():
test = Test('hello')
# AttributeError: 'Test' object has no attribute '__bar'
test.__bar()
# AttributeError: 'Test' object has no attribute '__foo'
print(test.__foo)


if __name__ == "__main__":
main()

However, Python doesn't strictly guarantee the privacy of private attributes or methods syntactically, it just gives private attributes and methods a different name to prevent access to them, and in fact you can still access them if you know the rules for changing the name, as the following code verifies.

class Test:

def __init__(self, foo):
self.__foo = foo

def __bar(self):
print(self.__foo)
print('__bar')


def main():
test = Test('hello')
test._Test__bar()
print(test._Test__foo)


if __name__ == "__main__":
main()

Object-oriented advancement

@property decorators

If you want to access a property you can do so through the getter (accessor) and setter (modifier) methods of the property. To do this, consider using @property wrappers to wrap getter and setter methods, making access to properties both safe and convenient, as shown in the code below.

class Person(object):

def __init__(self, name, age):
self._name = name
self._age = age

# Accessors - getter methods
@property
def name(self):
return self._name

# Accessors - getter methods
@property
def age(self):
return self._age

# Modifiers - setter methods
@age.setter
def age(self, age):
self._age = age

def play(self):
if self._age <= 16:
print('%s is playing flying chess.' % self._name)
else:
print('%s is playing Landlord.' % self._name)


def main():
person = Person('Wang Sledgehammer', 12)
person.play()
person.age = 22
person.play()
# person.name = 'Bai Yuanfang' # AttributeError: can't set attribute


if __name__ == '__main__':
main()

slots magic

If we need to qualify objects of a custom type to bind only certain properties, we can do so by defining slots variables in the class. Note that the slots qualification only works on objects of the current class, and does not have any effect on subclasses.

class Person(object):

# Restrict the Person object to bind only the _name, _age and _gender attributes
__slots__ = ('_name', '_age', '_gender')

def __init__(self, name, age):
self._name = name
self._age = age

@property
def name(self):
return self._name

@property
def age(self):
return self._age

@age.setter
def age(self, age):
self._age = age

def play(self):
if self._age <= 16:
print('%s is playing flying chess.' % self._name)
else:
print('%s is playing Landlord.' % self._name)


def main():
person = Person('Wang Sledgehammer', 22)
person.play()
person._gender = 'male'
# AttributeError: 'Person' object has no attribute '_is_gay'
# person._is_gay = True

Static methods

Static methods can be defined using the @staticmethod annotation to use methods by calling the class directly.

from math import sqrt


class Triangle(object):

def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c

@staticmethod
def is_valid(a, b, c):
return a + b > c and b + c > a and a + c > b

def perimeter(self):
return self._a + self._b + self._c

def area(self):
half = self.perimeter() / 2
return sqrt(half * (half - self._a) *
(half - self._b) * (half - self._c))


def main():
a, b, c = 3, 4, 5
# Static methods and class methods are called by sending messages to the class
if Triangle.is_valid(a, b, c):
t = Triangle(a, b, c)
print(t.perimeter())
# You can also call an object method by sending a message to the class but passing in the object receiving the message as an argument
# print(Triangle.perimeter(t))
print(t.area())
# print(Triangle.area(t))
else:
print('Unable to form triangle...')


if __name__ == '__main__':
main()

Class methods

Python can also define class methods in classes. The first argument to a class method is conventionally named cls, which represents an object with information about the current class (the class itself is also an object, sometimes called a class metadata object), and through this argument we can get information about the class and create an object of the class, as shown in the code below.

from time import time, localtime, sleep


class Clock(object):
"""digital clock""""

def __init__(self, hour=0, minute=0, second=0):
self._hour = hour
self._minute = minute
self._second = second

@classmethod
def now(cls):
ctime = localtime(time())
return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)

def run(self):
"""go word""""
self._second += 1
if self._second == 60:
self._second = 0
self._minute += 1
if self._minute == 60:
self._minute = 0
self._hour += 1
if self._hour == 24:
self._hour = 0

def show(self):
"""Show time""""
return '%02d:%02d:%02d' % \
(self._hour, self._minute, self._second)


def main():
# Create an object and get the system time via the class method
clock = Clock.now()
while True:
print(clock.show())
sleep(1)
clock.run()


if __name__ == '__main__':
main()

Relationships between classes

Simply put, there are three kinds of relationships between classes and classes: is-a, has-a and use-a relationships.

  • The is-a relationship is also called inheritance or generalisation; for example, the relationship between a student and a person, or a mobile phone and an electronic product are inheritance relationships.
  • If the relationship is between a whole and a part, then we call it an aggregation relationship; if the whole is further responsible for the life cycle of the part (the whole and the part are inseparable, co-existing and dying at the same time), then this is the strongest relationship and we This is called a synthetic relationship.
  • The use-a relationship is often called a dependency, e.g. if the driver has a driving action (method) which uses the car, then the relationship between the driver and the car is a dependency.

Inheritance

It is possible to create new classes based on existing classes, one way of doing this is to have one class inherit properties and methods directly from another class, thus reducing the need for repetitive code. The class that provides the inheritance information is called the parent class, or superclass or base class; the class that receives the inheritance information is called the child class, or derived class or derivative class. In addition to inheriting the properties and methods provided by the parent class, subclasses can also define their own unique properties and methods, so subclasses have more capabilities than the parent class. In practice, we often replace a parent class object with a subclass object, which is a common behavior in object-oriented programming, corresponding to the principle called [Richter's replacement principle](https://zh. wikipedia.org/wiki/Richter's substitution principle). Let's start by looking at an example of inheritance.

class Person(object):
"""person""""

def __init__(self, name, age):
self._name = name
self._age = age

@property
def name(self):
return self._name

@property
def age(self):
return self._age

@age.setter
def age(self, age):
self._age = age

def play(self):
print('%s is having a good time.' % self._name)

def watch_av(self):
if self._age >= 18:
print('%s is watching a love action movie.' % self._name)
else:
print('%s can only watch Bears.' % self._name)


class Student(Person):
"""Student""""

def __init__(self, name, age, grade):
super(). __init__(name, age)
self._grade = grade

@property
def grade(self):
return self._grade

@grade.setter
def grade(self, grade):
self._grade = grade

def study(self, course):
print('%s's %s is studying %s.' % (self._grade, self._name, course))


class Teacher(Person):
"""Teacher""""

def __init__(self, name, age, title):
super(). __init__(name, age)
self._title = title

@property
def title(self):
return self._title

@title.setter
def title(self, title):
self._title = title

def teach(self, course):
print('%s%s is teaching %s.' % (self._name, self._title, course))


def main():
stu = Student('Wang Sledgehammer', 15, 'Junior')
stu.study('Maths')
stu.watch_av()
t = Teacher('Luo Hao', 38, 'Bricklayer')
t.teach('Python Programming')
t.watch_av()


if __name__ == '__main__':
main()

Polymorphism

When a child class inherits a method from its parent, it can give a new implementation of the parent's existing methods, an action called method override. This action is called method override. By overriding a method, we can make the same behaviour of the parent class have different implementations in the child class, and when we call the overridden method, different objects of the child class will behave differently.

from abc import ABCMeta, abstractmethod


class Pet(object, metaclass=ABCMeta):
"""pet""""

def __init__(self, nickname):
self._nickname = nickname

@abstractmethod
def make_voice(self):
"""Make a sound""""
pass


class Dog(Pet):
"""Dog""""

def make_voice(self):
print('%s: woof woof woof...' % self._nickname)


class Cat(Pet):
"""Cat""""

def make_voice(self):
print('%s: Meow... Meow...' % self._nickname)


def main():
pets = [Dog('Wangcai'), Cat('Katie'), Dog('Rhubarb')]
for pet in pets:
pet.make_voice()


if __name__ == '__main__':
main()