Python study notes ten: object-oriented advanced end

1. __slots__ variable

  • Since Python is a dynamic language, in general, a dynamic language allows us to:
    • Bind new properties or methods to objects
    • You can also unbind the bound properties and methods
  • If we need to limit the object of a custom type to only bind certain attributes, we can limit it by defining the __slots__ variable in the class.
    • Note: The limitation of __slots__ is only effective for objects of the current class, and has no effect on subclasses.
class Student(object):
    
    # 用tuple定义允许绑定的属性名称
    # 限定Student对象只能绑定 name和age属性
    __slots__ = ('name', 'age')  


class GraduateStudent(Student):
    pass


s = Student()  # 创建新的实例
s.name = 'Jackson'  # 绑定属性'name'
s.age = 20  # 绑定属性'age'

# s.score = 100
# ERROR: AttributeError: 'Student' object has no attribute 'score'


# slots的限定只对当前类的对象生效,对子类并不起任何作用:
g = GraduateStudent()
g.score = 100
print('g.score =', g.score)		 # g.score = 100


2. Static methods and class methods

  • Before, the methods we defined in the class are all object methods, which means that these methods are all messages sent to the object.
  • In fact, the methods we write in the class do not need to be all object methods, for example:
    • We define a "triangle" class, construct a triangle by passing in three side lengths, and provide a method to calculate the perimeter and area
    • But the incoming three side lengths may not be able to construct a triangle object, so we can write a method to verify whether the three side lengths can form a triangle. This method is obviously not an object method, because the triangle object has not been created when this method is called. Come out (because I don’t know if the three sides can form a triangle)
    • So this method belongs to the triangle class, not the triangle object
  • We can use static methods to solve this type of problem:
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
    # 静态方法和类方法都是通过给类发消息来调用的
    if Triangle.is_valid(a, b, c):
        t = Triangle(a, b, c)
        print(t.perimeter())
        # 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数
        # print(Triangle.perimeter(t))
        print(t.area())
        # print(Triangle.area(t))
    else:
        print('无法构成三角形.')


if __name__ == '__main__':
    main()
    # 12
    # 6.0

  • Similar to static methods, Python can also define class methods in classes:
    • The first parameter of the class method is named cls by convention
    • It represents the object of the information related to the current class (the class itself is also an object, and in some places it is also called the metadata object of the class)
    • Through this parameter, we can obtain information related to the class and create objects of the class
from time import time, localtime, sleep


class Clock(object):
    """数字时钟"""

    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):
        """走字"""
        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):
        """显示时间"""
        return '%02d:%02d:%02d' % \
               (self._hour, self._minute, self._second)


def main():
    # 通过类方法创建对象并获取系统时间
    clock = Clock.now()
    while True:
        print(clock.show())
        sleep(1)
        clock.run()


if __name__ == '__main__':
    main()

3. Relationship between classes

  • Simply put, there are three types of relationships between classes: is-a, has-a, and use-a relationships:
    • The sa relationship (inheritance or generalization), such as the relationship between students and people, and the relationship between mobile phones and electronic products, are all inherited relationships.
    • The has-a relationship (association), such as the relationship between a department and an employee, and the relationship between a car and an engine, is an association relationship.
      • Aggregation relationship: if the association relationship is a whole and part association;
      • Synthetic relationship: If the whole is further responsible for the part of the life cycle (the whole and the part are inseparable, and the same will die at the same time), then this is the strongest relationship.
    • Use-a relationship (dependency), for example, the driver has a driving behavior (method), in which (parameter) uses the car, then the relationship between the driver and the car is a dependency relationship.
  • We can use UML (Unified Modeling Language) for object-oriented modeling. One of the important tasks is to describe the relationship between classes and classes with standardized graphic symbols.

4. Inheritance and Polymorphism

  • As we mentioned earlier, you can create new classes on the basis of existing classes. One way of doing this is to let one class directly inherit properties and methods from another class, thereby reducing the need for repetitive code writing.
  • The one that provides inheritance information is called the parent class, also called the super class or base class;
  • The ones that get the inheritance information are called subclasses, also called derived classes or derived classes.
    • In addition to inheriting the properties and methods provided by the parent class, the subclass can also define its own unique properties and methods, so the subclass has more capabilities than the parent class
    • Richter's substitution principle: In actual development, we often use subclass objects to replace a parent class object, which is a common behavior in object-oriented programming
    • The content of the Richter substitution principle can be described as: "A derived class (subclass) object can replace its base class (superclass) (parent class) object in the program."
  • Let's look at an example of inheritance:
# 父类:
class Person(object):
    """人"""

    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正在学习Python.' % self._name)

    def watch_mv(self):
        if self._age >= 18:
            print('%s可以打王者荣耀了.' % self._name)
        else:
            print('%s那就看看《喜羊羊与灰太狼》吧.' % self._name)

            
# 子类版本一:
class Student(Person):
    """学生"""

    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.' % (self._grade, self._name, course))


# 子类版本二:
class Teacher(Person):
    """老师"""

    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正在讲%s.' % (self._name, self._title, course))


def main():
    stu = Student('李元芳', 15, '高三')
    stu.study('数学')
    stu.watch_mv()
    t = Teacher('狄仁杰', 25, '大Boss')
    t.teach('如何成为Python大牛')
    t.watch_mv()


if __name__ == '__main__':
    main()
    # 高三的李元芳正在学习数学.
    # 李元芳那就看看《喜羊羊与灰太狼》吧.
    # 狄仁杰大Boss正在讲如何成为Python大牛.
    # 狄仁杰可以打王者荣耀了.
    
  • After the subclass inherits the methods of the parent class, it can give a new implementation version to the existing methods of the parent class. This action is called method override.
  • Through method rewriting, we can make the same behavior of the parent class have different implementation versions in the subclass. When we call this method overridden by the subclass, different subclass objects will show different behaviors. This is Polymorphism (poly-morphism).
from abc import ABCMeta, abstractmethod


class Pet(object, metaclass=ABCMeta):
    """宠物"""

    def __init__(self, nickname):
        self._nickname = nickname
    
    # 抽象方法
    @abstractmethod
    def make_voice(self):
        """发出声音"""
        pass


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

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


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

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


def main():
    pets = [Dog('旺财'), Cat('凯蒂'), Dog('阿黄')]
    for pet in pets:
        pet.make_voice()


if __name__ == '__main__':
    main()
    # 旺财: 汪汪汪...
    # 凯蒂: 喵...喵...
    # 阿黄: 汪汪汪...
    
  • In the above code, we processed the Pet class into an abstract class:
    • The so-called abstract class is a class that cannot create objects. This kind of class exists to allow other classes to inherit it.
  • From the grammatical level, Python does not provide support for abstract classes like Java or C#, but we can achieve the effect of abstract classes through the ABCMeta metaclass and abstractmethod wrapper of the abc module.
    • If there are abstract methods in a class, then the class cannot be instantiated (object creation)
  • In the above code, Dog and Cat two subclasses respectively rewrite the make_voice abstract method in the Pet class and give different implementation versions.
    • When we call this method in the main function, this method shows polymorphic behavior (the same method does different things)

Comprehensive case 1 (salary settlement system):

"""
某公司有三种类型的员工 分别是部门经理、程序员和销售员
需要设计一个工资结算系统,根据提供的员工信息来计算月薪
1.部门经理的月薪是每月固定15000元
2.程序员的月薪按本月工作时间计算,每小时150元
3.销售员的月薪是1200元的底薪加上销售额5%的提成
"""
from abc import ABCMeta, abstractmethod


class Employee(object, metaclass=ABCMeta):
    """员工"""

    def __init__(self, name):
        """
        初始化方法

        :param name: 姓名
        """
        self._name = name

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

    @abstractmethod
    def get_salary(self):
        """
        获得月薪

        :return: 月薪
        """
        pass


class Manager(Employee):
    """部门经理"""

    def get_salary(self):
        return 15000.0


class Programmer(Employee):
    """程序员"""

    def __init__(self, name, working_hour=0):
        super().__init__(name)
        self._working_hour = working_hour

    @property
    def working_hour(self):
        return self._working_hour

    @working_hour.setter
    def working_hour(self, working_hour):
        self._working_hour = working_hour if working_hour > 0 else 0

    def get_salary(self):
        return 150.0 * self._working_hour


class Salesman(Employee):
    """销售员"""

    def __init__(self, name, sales=0):
        super().__init__(name)
        self._sales = sales

    @property
    def sales(self):
        return self._sales

    @sales.setter
    def sales(self, sales):
        self._sales = sales if sales > 0 else 0

    def get_salary(self):
        return 1200.0 + self._sales * 0.05


def main():
    emps = [
        Manager('刘备'), Programmer('诸葛亮'),
        Manager('曹操'), Salesman('荀彧'),
        Salesman('吕布'), Programmer('张辽'),
        Programmer('赵云')
    ]
    for emp in emps:
        if isinstance(emp, Programmer):
            emp.working_hour = int(input('请输入%s本月工作时间: ' % emp.name))
        elif isinstance(emp, Salesman):
            emp.sales = float(input('请输入%s本月销售额: ' % emp.name))
        # 同样是接收get_salary这个消息但是不同的员工表现出了不同的行为(多态)
        print('%s本月工资为: ¥%s元' %
              (emp.name, emp.get_salary()))


if __name__ == '__main__':
    main()
    

Guess you like

Origin blog.csdn.net/amyniez/article/details/104572532