Capítulo 9: Orientación avanzada a objetos

Capítulo 9: Orientación avanzada a objetos

Ver artículos anteriores:

Capítulo 4: Estructura de bucle
Capítulo 5: Construcción de lógica de programa
Capítulo 6: Uso de funciones y módulos
Capítulo 7: Cadenas y estructuras de datos comunes
Capítulo 8: Conceptos básicos de programación orientada a objetos
O vaya a la columna "Tutorial de Python" para ver


Directorio de recursos: Código (9)
Descarga de recursos del artículo: (1-15 capítulos)
Enlace: https://pan.baidu.com/s/1Mh1knjT4Wwt__7A9eqHmng?pwd=t2j3
Código de extracción: t2j3


En los capítulos anteriores, hemos aprendido sobre el conocimiento introductorio de la orientación a objetos, cómo definir clases, cómo crear objetos y cómo enviar mensajes a los objetos. Para usar mejor las ideas de programación orientada a objetos para el desarrollo de programas, también necesitamos tener una comprensión más profunda de la programación orientada a objetos en Python.

@decorador de propiedades

Hemos discutido el problema del acceso a los atributos y métodos en Python antes. Aunque no recomendamos configurar los atributos como privados, también es problemático exponer los atributos directamente al mundo exterior. Por ejemplo, no tenemos forma de verificar si el valor asignado a un atributo es válido. . Nuestra sugerencia anterior era comenzar el nombre de la propiedad con un solo guión bajo. De esta manera, implica que la propiedad está protegida y no se recomienda el acceso directo desde el mundo exterior. Si desea acceder a la propiedad, puede usar el getter de la propiedad. (accesorio) y setter (modificador) ) para realizar la operación correspondiente. Si quiere hacer esto, puede considerar usar el envoltorio @property para envolver los métodos getter y setter, haciendo que el acceso a las propiedades sea seguro y conveniente, como se muestra en el código a continuación.

class Person(object):

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

    # 访问器 - getter方法
    @property
    def name(self):
        return self._name

    # 访问器 - getter方法
    @property
    def age(self):
        return self._age

    # 修改器 - setter方法
    @age.setter
    def age(self, age):
        self._age = age

    def play(self):
        if self._age <= 16:
            print('%s正在玩飞行棋.' % self._name)
        else:
            print('%s正在玩斗地主.' % self._name)


def main():
    person = Person('王大锤', 12)
    person.play()
    person.age = 22
    person.play()
    # person.name = '白元芳'  # AttributeError: can't set attribute


if __name__ == '__main__':
    main()

__tragamonedas__ magia

Estamos hablando aquí, no sé si te has dado cuenta de que Python es un lenguaje dinámico . Por lo general, los lenguajes dinámicos nos permiten vincular nuevas propiedades o métodos a objetos cuando el programa se está ejecutando y, por supuesto, desvincular propiedades y métodos ya vinculados. Pero si necesitamos limitar el objeto del tipo personalizado para vincular solo ciertas propiedades, podemos limitarlo definiendo la variable __slots__ en la clase. Cabe señalar que la limitación de __slots__ solo es válida para objetos de la clase actual y no tiene efecto en las subclases.

class Person(object):

    # 限定Person对象只能绑定_name, _age和_gender属性
    __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正在玩飞行棋.' % self._name)
        else:
            print('%s正在玩斗地主.' % self._name)


def main():
    person = Person('王大锤', 22)
    person.play()
    person._gender = '男'
    # AttributeError: 'Person' object has no attribute '_is_gay'
    # person._is_gay = True

métodos estáticos y métodos de clase

Anteriormente, los métodos que definimos en la clase eran todos métodos de objeto, es decir, estos métodos eran todos mensajes enviados al objeto. De hecho, los métodos que escribimos en la clase no necesitan ser todos métodos de objeto. Por ejemplo, definimos una clase de "triángulo", construimos un triángulo pasando las longitudes de tres lados y proporcionamos métodos para calcular el perímetro y el área, pero es posible que pasar Las longitudes de los tres lados no pueda construir un objeto triangular, por lo que primero podemos escribir un método para verificar si las longitudes de los tres lados pueden formar un triángulo. Este método obviamente no es un método de objeto, porque el objeto del triángulo no tiene aún se ha creado al llamar a este método (porque no sé si tres lados pueden formar un triángulo), por lo que este método pertenece a la clase de triángulo y no al objeto de triángulo. Podemos usar métodos estáticos para resolver este tipo de problema, el código se muestra a continuación.

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()

Al igual que los métodos estáticos, Python también puede definir métodos de clase en una clase. El primer parámetro de un método de clase se denomina cls, que representa un objeto de información relacionado con la clase actual (la clase en sí también es un objeto y, en algunos lugares, también se llama el objeto de metadatos de la clase), a través de este parámetro podemos obtener información relacionada con la clase y crear un objeto de la clase, el código es el siguiente.

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()

relación entre clases

En pocas palabras, hay tres tipos de relaciones entre clases: relaciones es-un, tiene-un y usa-un.

  • La relación es-un también se denomina herencia o generalización. Por ejemplo, la relación entre estudiantes y personas, y la relación entre teléfonos móviles y productos electrónicos son todas relaciones de herencia.
  • La relación "tiene un" generalmente se denomina asociación, como la relación entre departamentos y empleados, y la relación entre automóviles y motores son todas relaciones asociadas; si la relación es una asociación total y parcial, entonces la llamamos relación de agregación; si el todo es además responsable del ciclo de vida de una parte (el todo y la parte son inseparables, y ambos existen y mueren al mismo tiempo), entonces esta es la relación más fuerte, que llamamos relación compuesta.
  • La relación uso-a suele denominarse dependencia. Por ejemplo, si el conductor tiene un comportamiento de conducción (método) en el que (el parámetro) utiliza el coche, entonces la relación entre el conductor y el coche es una relación de dependencia.

Podemos usar algo llamado UML (Lenguaje de modelado unificado) para el modelado orientado a objetos.Una de las tareas importantes es describir la relación entre clases y clases con símbolos gráficos estandarizados. No daremos una introducción detallada a UML aquí, los lectores interesados ​​pueden leer el libro "Fundamentos del Diseño Orientado a Objetos UML" por sí mismos.

[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-Vz5Wz8Rx-1679200662992)(./res/uml-components.png)]

[Falló la transferencia de imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leeching, se recomienda guardar la imagen y cargarla directamente (img-cK8PjEEo-1679200662994)(./res/uml-example.png)]

Usando estas relaciones entre clases, podemos completar ciertas operaciones sobre la base de las clases existentes, o crear nuevas clases sobre la base de las clases existentes.Estos son medios importantes para lograr la reutilización del código. La reutilización del código existente no solo puede reducir la carga de trabajo del desarrollo, sino también facilitar la gestión y el mantenimiento del código, un medio técnico que todos utilizamos en nuestro trabajo diario.

Herencia y polimorfismo

Como mencionamos hace un momento, se pueden crear nuevas clases sobre la base de las clases existentes. Uno de los métodos es dejar que una clase herede propiedades y métodos directamente de otra clase, lo que reduce la escritura de código repetido. Las que proporcionan información de herencia se denominan clases padre, también denominadas superclases o clases base; las que obtienen información de herencia se denominan subclases, también denominadas clases derivadas o clases derivadas. Además de heredar las propiedades y los métodos provistos por la clase principal, la subclase también puede definir sus propias propiedades y métodos únicos, por lo que la subclase tiene más capacidades que la clase principal. En el desarrollo real, a menudo usamos objetos de subclase para reemplazar una clase principal. objeto de clase, este es un comportamiento común en la programación orientada a objetos, y el principio correspondiente se llama el principio de sustitución de Liskov . Veamos un ejemplo de herencia.

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正在愉快的玩耍.' % self._name)

    def watch_av(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_av()
    t = Teacher('骆昊', 38, '砖家')
    t.teach('Python程序设计')
    t.watch_av()


if __name__ == '__main__':
    main()

Una vez que la subclase hereda el método de la clase principal, puede proporcionar una nueva versión de implementación del método existente de la clase principal. Esta acción se denomina anulación del método. A través de la reescritura de métodos, podemos hacer que el mismo comportamiento de la clase principal tenga diferentes versiones de implementación en la subclase. Cuando llamamos al método reescrito por la subclase, los diferentes objetos de la subclase mostrarán diferentes comportamientos. Esto es polimorfismo (polimorfismo).

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()

En el código anterior, Pettratamos la clase como una clase abstracta. La llamada clase abstracta es una clase que no puede crear objetos. La existencia de esta clase es específicamente para que otras clases la hereden. Python no proporciona soporte para clases abstractas como Java o C# desde el nivel gramatical, pero podemos lograr el efecto de las clases abstractas a través de la metaclase y el envoltorio abcdel módulo.Si hay un método abstracto en una clase, no se puede instanciar la clase Transformación ( creación de objetos). En el código anterior, el método abstracto en la clase y las dos subclases se reescriben respectivamente y se les dan diferentes versiones de implementación. Cuando llamamos al método en la función, el método muestra un comportamiento polimórfico (el mismo método hace algo diferente).ABCMetaabstractmethodDogCatPetmake_voicemain

Caso integral

Caso 1: Ultraman vence a pequeños monstruos.

from abc import ABCMeta, abstractmethod
from random import randint, randrange


class Fighter(object, metaclass=ABCMeta):
    """战斗者"""

    # 通过__slots__魔法限定对象可以绑定的成员变量
    __slots__ = ('_name', '_hp')

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

        :param name: 名字
        :param hp: 生命值
        """
        self._name = name
        self._hp = hp

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

    @property
    def hp(self):
        return self._hp

    @hp.setter
    def hp(self, hp):
        self._hp = hp if hp >= 0 else 0

    @property
    def alive(self):
        return self._hp > 0

    @abstractmethod
    def attack(self, other):
        """攻击

        :param other: 被攻击的对象
        """
        pass


class Ultraman(Fighter):
    """奥特曼"""

    __slots__ = ('_name', '_hp', '_mp')

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

        :param name: 名字
        :param hp: 生命值
        :param mp: 魔法值
        """
        super().__init__(name, hp)
        self._mp = mp

    def attack(self, other):
        other.hp -= randint(15, 25)

    def huge_attack(self, other):
        """究极必杀技(打掉对方至少50点或四分之三的血)

        :param other: 被攻击的对象

        :return: 使用成功返回True否则返回False
        """
        if self._mp >= 50:
            self._mp -= 50
            injury = other.hp * 3 // 4
            injury = injury if injury >= 50 else 50
            other.hp -= injury
            return True
        else:
            self.attack(other)
            return False

    def magic_attack(self, others):
        """魔法攻击

        :param others: 被攻击的群体

        :return: 使用魔法成功返回True否则返回False
        """
        if self._mp >= 20:
            self._mp -= 20
            for temp in others:
                if temp.alive:
                    temp.hp -= randint(10, 15)
            return True
        else:
            return False

    def resume(self):
        """恢复魔法值"""
        incr_point = randint(1, 10)
        self._mp += incr_point
        return incr_point

    def __str__(self):
        return '~~~%s奥特曼~~~\n' % self._name + \
            '生命值: %d\n' % self._hp + \
            '魔法值: %d\n' % self._mp


class Monster(Fighter):
    """小怪兽"""

    __slots__ = ('_name', '_hp')

    def attack(self, other):
        other.hp -= randint(10, 20)

    def __str__(self):
        return '~~~%s小怪兽~~~\n' % self._name + \
            '生命值: %d\n' % self._hp


def is_any_alive(monsters):
    """判断有没有小怪兽是活着的"""
    for monster in monsters:
        if monster.alive > 0:
            return True
    return False


def select_alive_one(monsters):
    """选中一只活着的小怪兽"""
    monsters_len = len(monsters)
    while True:
        index = randrange(monsters_len)
        monster = monsters[index]
        if monster.alive > 0:
            return monster


def display_info(ultraman, monsters):
    """显示奥特曼和小怪兽的信息"""
    print(ultraman)
    for monster in monsters:
        print(monster, end='')


def main():
    u = Ultraman('骆昊', 1000, 120)
    m1 = Monster('狄仁杰', 250)
    m2 = Monster('白元芳', 500)
    m3 = Monster('王大锤', 750)
    ms = [m1, m2, m3]
    fight_round = 1
    while u.alive and is_any_alive(ms):
        print('========第%02d回合========' % fight_round)
        m = select_alive_one(ms)  # 选中一只小怪兽
        skill = randint(1, 10)   # 通过随机数选择使用哪种技能
        if skill <= 6:  # 60%的概率使用普通攻击
            print('%s使用普通攻击打了%s.' % (u.name, m.name))
            u.attack(m)
            print('%s的魔法值恢复了%d点.' % (u.name, u.resume()))
        elif skill <= 9:  # 30%的概率使用魔法攻击(可能因魔法值不足而失败)
            if u.magic_attack(ms):
                print('%s使用了魔法攻击.' % u.name)
            else:
                print('%s使用魔法失败.' % u.name)
        else:  # 10%的概率使用究极必杀技(如果魔法值不足则使用普通攻击)
            if u.huge_attack(m):
                print('%s使用究极必杀技虐了%s.' % (u.name, m.name))
            else:
                print('%s使用普通攻击打了%s.' % (u.name, m.name))
                print('%s的魔法值恢复了%d点.' % (u.name, u.resume()))
        if m.alive > 0:  # 如果选中的小怪兽没有死就回击奥特曼
            print('%s回击了%s.' % (m.name, u.name))
            m.attack(u)
        display_info(u, ms)  # 每个回合结束后显示奥特曼和小怪兽的信息
        fight_round += 1
    print('\n========战斗结束!========\n')
    if u.alive > 0:
        print('%s奥特曼胜利!' % u.name)
    else:
        print('小怪兽胜利!')


if __name__ == '__main__':
    main()

Caso 2: Juego de póquer.

import random


class Card(object):
    """一张牌"""

    def __init__(self, suite, face):
        self._suite = suite
        self._face = face

    @property
    def face(self):
        return self._face

    @property
    def suite(self):
        return self._suite

    def __str__(self):
        if self._face == 1:
            face_str = 'A'
        elif self._face == 11:
            face_str = 'J'
        elif self._face == 12:
            face_str = 'Q'
        elif self._face == 13:
            face_str = 'K'
        else:
            face_str = str(self._face)
        return '%s%s' % (self._suite, face_str)
    
    def __repr__(self):
        return self.__str__()


class Poker(object):
    """一副牌"""

    def __init__(self):
        self._cards = [Card(suite, face) 
                       for suite in '♠♥♣♦'
                       for face in range(1, 14)]
        self._current = 0

    @property
    def cards(self):
        return self._cards

    def shuffle(self):
        """洗牌(随机乱序)"""
        self._current = 0
        random.shuffle(self._cards)

    @property
    def next(self):
        """发牌"""
        card = self._cards[self._current]
        self._current += 1
        return card

    @property
    def has_next(self):
        """还有没有牌"""
        return self._current < len(self._cards)


class Player(object):
    """玩家"""

    def __init__(self, name):
        self._name = name
        self._cards_on_hand = []

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

    @property
    def cards_on_hand(self):
        return self._cards_on_hand

    def get(self, card):
        """摸牌"""
        self._cards_on_hand.append(card)

    def arrange(self, card_key):
        """玩家整理手上的牌"""
        self._cards_on_hand.sort(key=card_key)


# 排序规则-先根据花色再根据点数排序
def get_key(card):
    return (card.suite, card.face)


def main():
    p = Poker()
    p.shuffle()
    players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')]
    for _ in range(13):
        for player in players:
            player.get(p.next)
    for player in players:
        print(player.name + ':', end=' ')
        player.arrange(get_key)
        print(player.cards_on_hand)


if __name__ == '__main__':
    main()

Explicación: puede intentar escribir un juego de póquer simple basado en el código anterior, como el blackjack, y puede encontrar las reglas del juego en Internet.

Caso 3: Sistema de liquidación de salarios.

"""
某公司有三种类型的员工 分别是部门经理、程序员和销售员
需要设计一个工资结算系统 根据提供的员工信息来计算月薪
部门经理的月薪是每月固定15000元
程序员的月薪按本月工作时间计算 每小时150元
销售员的月薪是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()

Supongo que te gusta

Origin blog.csdn.net/xyx2023/article/details/129649256
Recomendado
Clasificación