IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> Python学习日志11 - 面对对象编程 -> 正文阅读

[Python知识库]Python学习日志11 - 面对对象编程

Python学习日志

RBHGO的主页欢迎关注

温馨提示:创作不易,如有转载,注明出处,感谢配合~

目录

前言

装饰器是一种Python中的特色语法,可以通过装饰器来增强现有的类或函数,这是一种非常有用的编程技巧。因为它在函数和类中都有实现,我会在这片日志分享中进行介绍。

进入正题

面向对象编程是一种非常流行的编程范式(programming paradigm),所谓编程范式就是程序设计的方法学,也就是程序员对程序的认知和理解。

前面的分享中我们说过“程序是指令的集合”,运行程序时,程序中的语句会变成一条或多条指令,然后由CPU(中央处理器)去执行。为了简化程序的设计,我们又讲到了函数,把相对独立且经常重复使用的代码放置到函数中,在需要使用这些代码的时候调用函数即可。如果一个函数的功能过于复杂和臃肿,我们又可以进一步将函数进一步拆分为多个子函数来降低系统的复杂性。

Python学习日志11课 - 面对对象编程

"""
Python学习日志11课 - 面向对象的编程

指令式编程 ---> 面向过程(函数)编程 ---> 适用于程序比较简单
编程范式(程序设计的方法论)︰面向对象编程 / 函数式编程

对象:对象是可以接收消息的实体,面向对象编程就是通过给对象发消息达到解决问题的目标。
    - 对象 = 数据+方法(函数) ---> 对象将数据和操作数据的函数从逻辑上变成了一个整体

类:将一大类对象共同的特征(静态特征和动态特征)抽取出来之后得到的一个抽象概念。简单的说,
类是对象的蓝图(模板),有了类才能够创建出这种类型的对象。
面向对象编程
    - 1.定义类型
        ~ 类的命名使用驼峰命名法(每个单词首字母大写)
        ~ 数据抽象:找到和对象相关的静态特征(属性)
        ~ 行为抽象:找到和对象相关的动态特征(方法)
    - 2.创造对象
        ~ —切皆为对象
        ~ 对象都有属性和行为
        ~ 每个对象都是独一无二的
        ~ 对象—定属于某个类
    - 3.发送消息

Author: RBHGO
Declaration: Mia San Mia ~~~
"""

一.定义类

在Python中,可以使用class关键字加上类名来定义类,通过缩进我们可以确定类的代码块,就如同定义函数那样。写在类里面的函数除了必须的属性另外的我们通常称之为方法,方法就是对象的行为,也就是对象可以接收的消息。方法的第一个参数通常都是self,它代表了接收这个消息的对象本身。

__init__方法通常也被称为初始化方法,在我们调用Student类的构造器创建对象时,首先会在内存中获得保存学生对象所需的内存空间,然后通过自动执行__init__方法,完成对内存的初始化操作,也就是把数据放到内存空间中。所以我们可以通过给Student类添加__init__方法的方式为学生对象指定属性,同时完成对属性赋初始值的操作。

# 第一步:定义类
class Student:

    # 数据抽象(属性)
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # 行为抽象(方法)
    def eat(self):
        """吃饭"""
        print(f'{self.name}正在吃饭.')

    def study(self, course_name):
        """学习"""
        print(f'{self.name}正在学习{course_name}. ')

    def play(self, game_name):
        """玩游戏"""
        print(f'{self.name}正在玩{game_name}. ')

    def watch_American_TV(self, TV_name):
        """看电影"""
        if self.age < 18:
            print(f'{self.name}未满16岁,只能看《小猪佩奇》.')
        else:
            print(f'{self.name}正在观看{TV_name}.')

二.创建对象

在我们定义好一个类之后,可以使用构造器语法来创建对象,代码如下所示。

"""
堆(heap) ---> 我们通过构造器语法创建的对象基本都在堆空间,而对象的引用通常是
                放在栈上的,通过对象引用(变量)就可以访问到对象并向其发送消息
"""

# 第二步︰创建对象 ---> 构造器语法 ---> 类名(...,...)
stu1 = Student('GG_bond', 15)
stu2 = Student('RBHGO', 20)

三.给对象发消息

给对象发消息也称为调用对象的方法。

# 通过“对象.方法”调用方法,点前面的对象就是接收消息的对象,只需要传入第二个参数
stu1.study('Python算法与程序设计')         # 运行结果:    GG_bond正在学习Python算法与程序设计. 
stu1.eat()				   				  # 运行结果:   GG_bond正在吃饭.
stu2.play('GTA5')     					   # 运行结果:  RBHGO正在玩GTA5. 
stu2.watch_American_TV('权力的游戏')		     # 运行结果: RBHGO正在观看权力的游戏.
stu1.watch_American_TV('越狱')			    # 运行结果:GG_bond未满16岁,只能看《小猪佩奇》.

打印对象

在Python中,以两个下划线__(读作dunder)开头和结尾的方法通常都是有特殊用途和意义的方法,我们一般称之为魔术方法魔法方法

"""
魔术(魔法)方法 ---> 以__开头以__结尾 ---> 有特殊用途和意义的方法
    - __init__ ---> 初始化方法,在调用构造器语法创建对象的时候会被自动调用
        ~ (initialize -> 初始化)
    - __str__ ---> 获得对象的字符串表示,在调用 print函数直接输出对象时会被自动调用
    - __repr__ ---> 获得对象的字符串表示,把对象放到容器中,用 print直接输出容器时会自动调用
        ~ (representation -> 表述)
    - __lt__ ---> 对应小于号运算符,自动比大小
        ~ (less than -> '<'运算符)
        
Author: RBHGO
"""
class Card:
    """牌"""

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

    # 获得对象的字符串表示
    def __str__(self):
        return self.display()

    def __repr__(self):
        return self.display()

    def __lt__(self, other):
        # 魔术方法---> less than ---> <运算符
        face1, face2 = self.face, other.face
        return face1 < face2

    def display(self):
        """显示牌"""
        suites = {'S': '?', 'H': '?', 'C': '?', 'D': '?'}
        faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', '7', 'Q', 'K']
        return f'{suites[self.suite]}{faces[self.face]}'


def main():
    """程序的入口"""
    card1 = Card('H', 5)
    card2 = Card('S', 12)
    card3 = Card('C', 9)
    card4 = Card('D', 7)
    print(card1.display(), card2.display())
    # __str__魔法方法的作用
    print(card1, card2)
    # __repr__魔法方法的作用
    cardA = [card1, card2, card3, card4]
    print(cardA)


if __name__ == '__main__':
    main()

装饰器

装饰器是Python中用一个函数装饰另外一个函数或类并为其提供额外功能的语法现象。装饰器本身是一个函数,它的参数是被装饰的函数或类,它的返回值是一个带有装饰功能的函数。很显然,装饰器是一个高阶函数,它的参数和返回值都是函数。

我们来做一个可用装饰器的题定义描述三角形的类,提供计算周长和面积的方法。

"""
我们在类里面写的函数,通常称之为方法,它们基本上都是发给对象的消息。
但是有的时候,我们的消息并不想发给对象,而是希望发给这个类(类本身也是一个对象),
这个时候,我们可以使用静态方法或类方法。

静态方法 - 发给类的消息 ---> @staticmethod ---> 装饰器
类方法 - 发给类的消息 ---> @classmethod ---> 装饰器 ---> 第一个参数(cls)是接收消息的类

Author: RBHGO
"""

# 定义三角形类
class Triangle:

    def __init__(self, a, b, c):

        self.a = a
        self.b = b
        self.c = c

    # 装饰器 2个装饰器效果一样,@classmethod多一个cls参数
    # @classmethod
    # def is_valid(cls, a, b, c):
        # return a + b > c and b + c > a and a + c > b
        
    @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):
        """计算面积"""
        # p = (self.len1 + self.len2 + self.len3) / 2
        # 直接调用计算周长的方法
        p = self.perimeter() / 2
        return (p * (p - self.a) * (p - self.b) * (p - self.c)) ** 0.5


if __name__ == '__main__':
    if Triangle.is_valid(1, 4, 5):
        t = Triangle(3, 4, 5)
        print(t.perimeter())
        print(t.area())
    else:
        print('无效的边长,无法构造三角形对象')

我们在类中进行判断的时候,首先类没有完全建立,也就是说里面的函数还是面对对象,在面对对象都无法完成时,明显类无法建立,自然值也传不到类上面,所以我们要用装饰器来解决这个问题;将判断是否能构造三角形的问题控制到发送给对象,这样避免了程序不能顺利运行的尴尬。

小练

1.通过面对对象的方法,计算过道和围墙的造价。

import math


class Circle:

    def __init__(self, radius):
        self.radius = radius

    def perimeter(self):
        """计算周长"""
        # print(f'{self.name}的周长是{self.radius * 2 * 3.14}')
        return 2 * math.pi * self.radius

    def area(self):
        """计算面积"""
        # print(f'{self.name}的面积是{self.radius ** 2 * 3.14}')
        return math.pi * self.radius ** 2


if __name__ == '__main__':
    r = float(input('请输入游泳池的半径: '))
    c1, c2 = Circle(r), Circle(r + 3)

    fence_price = c2.perimeter() * 38.5
    aisle_price = ((c2.area()-c1.area()) * 58.5)
    print(f'围墙的造价是{fence_price:.2f}元')
    print(f'过道的造价是{aisle_price:.2f}元')

2.定义一个时钟类(要让钟走起来)

#创建一个时钟对象(可以显示时/分/秒),让它运转起来(走字)。
import time


# 定义数字时钟类
class Clock(object):
    """数字时钟"""

    def __init__(self, hour=0, minute=0, second=0):
        """初始化方法
        :param hour: 小时
        :param minute: 分种
        :param second: 秒种
        """
        self.hour = hour
        self.minute = minute
        self.second = second

    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 f'{self.hour:0>2d}:{self.minute:0>2d}:{self.second:0>2d}'


# 创建时钟对象
clock = Clock(11, 46, 10)
while True:
    # 给时钟对象发消息读取时间
    print(clock.show())
    # 休眠1秒钟
    time.sleep(1)
    # 给时钟对象发消息使其走字
    clock.run()

面对对象的四大支柱

面向对象编程的四大支柱:

  • 抽象(abstraction): 提取共性(定义类就是一个抽象过程,需要做数据抽象和行为抽象)。
  • 封装(encapsulation): 把数据和操作数据的函数从逻辑上组装成一个整体(对象)。
    • 隐藏实现细节,暴露简单的调用接口。
  • 继承(inheritance): 扩展已有的类创建新类,实现对已有类的代码复用。
  • 多态(polymorphism): 给不同的对象发出同样的消息,不同的对象执行了不同的行为。
(1).两个类之间的关系
"""

- is-a关系:继承 ---> 一个类派生出另一个类
    class Teacher(Person)
    
- has-a关系:关联 ---> 把一个类的对象作为另外一个类的对象的属性
    a car has an engine.
    ~ (普通)关联:
    ~ 强关联:整体和部分的关系 ---> 聚合(整体对部分的生命周期不负责任)和合成(整体对部分的生命周期负责任)
    
- use-a关系∶依赖 ---> —个类的对象作为另外—个类的方法的参数或返回值
    a person use a vehicle

Author: RBHGO
"""

# 马、摩托车与交通工具是继承关系,唐先生有三个徒弟是普通关联关系,引擎与摩托车是强关联关系,
# 唐先生使用了马或者摩托车,这是依赖关系


class Vehicle:
    """交通工具"""
    pass


class Horse(Vehicle):
    """马"""
    pass


class Motobike(Vehicle):
    """摩托车"""

    def __init__(self):
        self.engine = Engine()


class Engine:
    """引擎"""
    pass


class Follower:
    """徒弟"""
    pass


class MrTang:
    """唐僧"""

    def __init__(self):
        self.followers = []

    def drive(self, vehicle):
        """驾驶"""
        pass
(2).继承

对于打印对象中牌类的问题,如果我们想按照黑桃梅方的顺序整理手牌,那么或许通过继承内置函数enum枚举时最好的方法。

"""
枚举

如果一个变量的取值只有有限个选项,可以考虑使用枚举类型。
Python中没有定义枚举类型的语法,但是可以通过继承Enum类来实现枚举类型。
    - 结论1:枚举类型是定义符号常量的最佳选择! ! !
    - 结论2:符号常量(有意义的名字)总是优于字面常量! ! !

Author: RBHGO
"""
from enum import Enum


# 枚举类型
class Suite(Enum):
    SPADE, HEART, CLUB, DIAMOND = range(4)


class Card:
    """牌"""

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

    def __str__(self):
        return self.show()

    def __repr__(self):
        return self.show()

    def __lt__(self, other):
        if self.suite == other.suite:
            return self.face < other.face
        return self.suite.value < other.suite.value

    def show(self):
        """显示"""
        suites = ['?', '?', '?', '?']
        faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
        return f'{suites[self.suite.value]}{faces[self.face]}'


def main():
    """程序入口"""
    card1 = Card(Suite.HEART, 1)
    card2 = Card(Suite.SPADE, 13)
    print(card1, card2)
    print(card1 is card2)
    card3 = Card(Suite.DIAMOND, 9)
    card4 = Card(Suite.CLUB, 11)
    print(card3.show(), card4.show())
    card1 = card2
    print(card1, card2, card3)
    # 身份运算符
    print(card1 is card2)
    print(card1 is card3)
    cards = [card1, card2, card3, card4]
    print(cards)


if __name__ == '__main__':
    main()

继承的语法是在定义类的时候,在类名后的圆括号中指定当前类的父类。如果定义一个类的时候没有指定它的父类是谁,那么默认的父类是object类。object类是Python中的顶级类,这也就意味着所有的类都是它的子类,要么直接继承它,要么间接继承它。下面是对继承详细的介绍和应用:

"""

继承:对已有的类进行扩展创建出新的类,这个过程就叫继承。
提供继承信息的类叫做父类(超类、基类),得到继承信息的类称为子类(派生类)。
Python中的继承允许多重继承,一个类可以有一个或多个父类。
建议如果不是必须使用多重继承的场景下,请尽量使用单一继承。
如果使用了多重继承的场景应该尽量避免出现`菱形继承`的情况,否则代码的可读性和可理解性都会变得非常的糟糕。


继承是实现代码复用的一种手段,但是千万不要滥用继承。
    - 继承是—种is-a关系。
        ~ a student is a person.
        ~ a teacher is a person.
        ~ a programmer is a person.
子类直接从父类继承公共的属性和行为,再添加自己特有的属性和行为,
所以子类一定是比父类更强大的,任何时候都可以用子类对象去替代父类对象。

super() ---> super()函数在子类中调用父类的初始化方法

Author: RBHGO
"""


class Person:
    """人"""

    # 数据抽象(属性)
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # 行为抽象(方法)
    def eat(self):
        """吃饭"""
        print(f'{self.name}正在吃饭.')

    def study(self, course_name):
        """学习"""
        print(f'{self.name}正在学习{course_name}. ')

    def play(self, game_name):
        """玩游戏"""
        print(f'{self.name}正在玩{game_name}. ')

    def watch_American_TV(self, TV_name):
        """看电影"""
        if self.age < 18:
            print(f'{self.name}未满16岁,只能看《小猪佩奇》.')
        else:
            print(f'{self.name}正在观看{TV_name}.')


# 继承父类 人的行为
class Teacher(Person):

    def __init__(self, name, age, sex, tetel):
        self.name = name
        self.age = age
        self.sex = sex
        self.tetel = tetel

    def teach(self, course_name):
        """教学"""
        print(f'{self.tetel}{self.name}正在讲授{course_name}. ')


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

    def __init__(self, name, age, sex):
        # super方法进行继承父类的数据抽象,调用父类的初识方法
        super().__init__(name, age)
        self.sex = sex

    def write_cede(self, programming_lauguage):
        """写代码"""
        print(f'{self.age}{self.sex}{self.name}正在用{programming_lauguage}写代码. ')


def main():
    tcr1 = Teacher('金轮', 38, 'male', '金牌讲师')
    tcr2 = Teacher('张三', 31, 'male', '法外狂徒')
    tcr1.teach('《母猪的产后护理》')
    tcr2.play('人类一败涂地')
    tcr2.teach('《吸烟有害身体健康》')
    tcr1.watch_American_TV('《斯巴达克斯》')

    prog1024 = Programmer('RBHGO', 20, '男性')
    prog1024.write_cede('Python3')
    prog1024.eat()


if __name__ == '__main__':
    main()
(3).多态

下面是对多态应用的例子:

"""
例子 ---> 工资(月薪)结算系统
三类员工:
    ~ 部门经理:固定月薪,15000元
    ~ 程序员:计时结算月薪,每小时200元
    ~ 销售员:底薪 + 提成,底薪1800元,销售额 5%提成
录入员工信息,自动结算月薪

子类对父类已有的方法,重新给出自己的实现版本,这个过程叫做方法重写(override)。
重写方法的过程中,不同的子类可以对父类的同一个方法给出不同的实现版本,那么该方法在运行时就会表现出多态行为。
多态(polymorphism):给不同的对象发出同样的消息,不同的对象执行了不同的行为。

面向对象编程的四大支柱:
~ 抽象(abstraction): 提取共性(定义类就是一个抽象过程,需要做数据抽象和行为抽象)。
~ 封装(encapsulation): 把数据和操作数据的函数从逻辑上组装成一个整体(对象)。
    - 隐藏实现细节,暴露简单的调用接口。
~ 继承(inheritance): 扩展已有的类创建新类,实现对已有类的代码复用。
~ 多态(polymorphism): 给不同的对象发出同样的消息,不同的对象执行了不同的行为。


Author: RBHGO
"""
from abc import abstractmethod


class Employee:
    """雇员"""

    def __init__(self, no, name):
        self.name = name
        self.no = no

    # 告诉子类一定要创建这个方法
    # 装饰器
    @abstractmethod
    def get_salary(self):
        """获得月薪"""
        pass


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

    def __init__(self, name, no):
        super().__init__(name, no)

    def manage(self):
        """管理部门"""
        pass

    def get_salary(self):
        return 15000


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

    def __init__(self, name, no):
        super().__init__(name, no)
        self.working_hour = 240

    def write_code(self):
        """敲代码"""
        pass

    def get_salary(self):
        return 200 * self.working_hour


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

    def __init__(self, name, no):
        super().__init__(name, no)
        self.sales_volume = 0

    def product_sales(self):
        """产品销售"""
        pass

    def get_salary(self):
        return 1800 + 0.05 * self.sales_volume


def main():
    emps = [
        Manager(3091, '大傻'),
        Programmer(3082, '二麻'),
        Salesman(3018, '李美丽')
    ]
    for emp in emps:
        if type(emp) == Programmer:
            emp.working_hour = int(input(f'请输入{emp.name}本月工作时长:'))
        elif type(emp) == Salesman:
            emp.sales_volume = float(input(f'请输入{emp.name}本月销售额:'))
        print(f' {emp.name}本月工资:{emp.get_salary()}元')


if __name__ == '__main__':
    main()

总结

面向对象编程是一种非常流行的编程范式,除此之外还有指令式编程函数式编程等编程范式。由于现实世界是由对象构成的,而对象是可以接收消息的实体,所以面向对象编程更符合人类正常的思维习惯。但是要想灵活运用面向对象编程中的抽象、封装、继承、多态需要长时间的积累和沉淀。Python是动态语言,Python中的对象可以动态的添加属性。在面向对象的世界中,一切皆为对象,我们定义的类也是对象,所以类也可以接收消息,对应的方法是类方法或静态方法。通过继承,我们可以从已有的类创建新类,实现对已有类代码的复用。

感谢学习陪伴,您的点赞,评论就是我更新的动力

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2021-08-09 10:12:00  更:2021-08-09 10:12:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 14:37:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码