Python第二语言(十一、Python面向对象(下))

目录

1. 封装

1.1 私有成员:__成员、__成员方法

2. 继承:单继承、多继承

2.1 继承的基础语法

2.2 复写 & 子类使用父类成员

3. 变量的类型注解:给变量标识变量类型

3.1 为什么需要类型注解

3.2 类型注解

3.3 类型注解的语法

3.4 类型的注解限制

4. 函数和方法类型的注解:给函数和方法标识数据类型

4.1 函数(方法)的类型注解-形参注解

4.2 函数(方法)的类型注解-返回值注解

5. Union联合类型注解:给变量、函数和方法、形参和返回值标识联合类型注解

5.1 Union概念

5.2 Union使用

6. 多态

6.1 多态使用

6.2 抽象类(接口)

7. 案例:使用面向对象概念实现

7.1 数据分析案例-文件读取

7.2 数据分析案例-数据计算

7.3 数据分析案例-可视化开发


导航:

Python第二语言(一、Python start)-CSDN博客

Python第二语言(二、Python语言基础)-CSDN博客

Python第二语言(三、Python函数def)-CSDN博客

Python第二语言(四、Python数据容器)-CSDN博客

Python第二语言(五、Python文件相关操作)-CSDN博客

Python第二语言(六、Python异常)-CSDN博客

Python第二语言(七、Python模块)-CSDN博客

Python第二语言(八、Python包)-CSDN博客

Python第二语言(九、Python第一阶段实操)-CSDN博客

Python第二语言(十、Python面向对象(上))-CSDN博客

Python第二语言(十一、Python面向对象(下))-CSDN博客

Python第二语言(十二、SQL入门和实战)-CSDN博客

Python第二语言(十三、PySpark实战)-CSDN博客

Python第二语言(十四、高阶基础)-CSDN博客

面向对象编程,是许多编程语言都支持的一种编程思想;

基本思想:基于模板(类)去创建实体(对象),使用对象完成功能开发;

1. 封装

  • 封装表示的是,将现实世界事物的:属性、行为,封装到类中,描述位成员变量和成员方法;
  • 对用户隐藏的属性和行为:比如电视机的外壳,内部就封装了我们用户看不见的电线;
1.1 私有成员:__成员、__成员方法

既然现实事务由不公开的属性和行为,那么作为现实事务在程序中映射的类,也应该支持;

类中提供了私有成员的形式来支持:私有成员变量、私有成员方法;

  • 定义私有成员的方式:

    私有成员变量:变量名以__开头(2个下划线)

    私有成员方法:方法名以__开头(2个下划线)

1. 私有方法无法直接被类对象使用,私有变量无法赋值,也无法获取值;

class phone:
    IMEI = None  # 序列号
    producer = None  # 厂商


    __current_voltage = None  # 私有:当前私有属性

    def call_by_5g(self):
        print("5G通话已开启")

    def __keep_single_core(self):  # 私有:当前私有方法
        print("让CPU以单核模式运行以节省电量")


phone = Phone()  # 创建对象
phone.__keep_single_core()  # 使用私有方法,无法使用
phone.__current_voltage = 220  # 私有变量赋值
print(phone.__current_voltage)  # 获取私有变量,无法使用

2. 成员变量的使用;

使用公共方法来访问类的私有属性;在类的内部其他成员可以随便使用类的私有属性或方法;

class phone:
    __current_voltage = None  # 私有:当前私有属性

    def __keep_single_core(self):  # 私有:当前私有方法
        print("让CPU以单核模式运行以节省电量")

    def call_by_5g(self):
        if self.__current_voltage() >= 1:  # 可以使用私有成员
            print("5G通话已开启")
        else:
            self.__keep_single_core()
            print("无法使用5G通话")

小结:

  1. 为什么是私有成员,为什么需要私有成员?

    现实事务有部分属性和行为是不公开对使用者开放的,同样在类中描述属性和方法的时候也需要达到这个要求,就需要定义私有变量成员了;

  2. 如何定义私有成员:成员变量和成员方法的命名以_ _开头即可;

  3. 私有成员的访问限制:类对象无法访问私有成员,类中的其他成员可以访问私有成员;

  4. 对类封装私有的类的内部成员,不对外开放,实现封装的概念;

  5. 如没必要的私有方法或属性没必要展示给用户看,或没必要给程序员调用该私有方法或私有属性

2. 继承:单继承、多继承

2.1 继承的基础语法
  • 继承的引出:由一代代不断迭代的产品中,基于老款的设计图,进行修改;
  • 继承分为:单继承和多继承;
  • 继承表示:将从父类那里继承(复制)来成员变量和方法(不包含私有的)

1. 继承的写法:class 子类(继承的父类):

2. 单继承class 子类(父类)

  • 继承别的类的叫做子类,被继承的叫做父类;

实现单继承:

class Phone:  # 单继承
    IMEI = None
    producer = "HW"

    def call_by_4g(self):
        print("4g通话")


class Phone2024(Phone):
    face_id = "10001"

    def call_by_5g(self):
        print("2024开始5g通话")


phone = Phone2024()
print(phone.producer)
phone.call_by_4g()
phone.call_by_5g()

3. 多继承class 子类(父类1, 父类2, 父类3)

  • 由单个子类继承多个父类,实现所有继承父类的公共方法和公共属性;
  • pass 语法:代表这是空的,这里什么都没有,让代码不报错;
  • 多继承的注意事项:如果父类中存在同名的属性怎么办?会默认按照继承顺序从左到右为优先级调用
class Phone:  # 单继承
    IMEI = None
    producer = "HW"

    def call_by_4g(self):
        print("4g通话")


class MIUIPhone:
    def read_card(self):
        print("小米手机,贴近生活")


class HuaweiPhone:
    def control(self):
        print("华为手机,值得拥有")
        
        
class Phone2024(Phone, MIUIPhone, HuaweiPhone):  # 多继承
    pass


phone = Phone2024()
phone.call_by_4g()  # 1.继承手机
phone.read_card()  # 2.继承小米手机
phone.control()  # 3.继承华为手机

小结:

  • 继承就是一个类继承另外一个类的成员变量和方法;
    • 语法class 子类(父类1, 父类2, ...)
    • 子类构建的类对象:可以有自己的成员变量和成员方法;子类可以使用父类的成员变量和方法;
  • 单继承和多继承:
    • 单继承:一个类继承另一个类;
    • 多继承:一个类继承多个类,按照顺序从左往右依次继承;
    • 多继承中,如果父类有同名的方法或属性,先继承的优先级高于后继承;
2.2 复写 & 子类使用父类成员

1. 复写:就是单纯的在子类中重写父类的属性和方法,实现复写(一般都是子类继承父类后,需求不满足,那么可以进行复写);

class Phone:
    IMEI = None

    def call_by_5g(self):
        print("父类5g通话")


class Phone2024(Phone):
    IMEI = "10001"  # 复写父类属性

    def call_by_5g(self):  # 复写父类方法
        print("子类重写5g通话,视频通话")


phone = Phone2024()
phone.call_by_5g()

2. 子类中调用父类成员

  • 一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员,如果想要使用被复写的父类成员,就需要特殊的调用方式;
  1. 调用父类成员:
    • 使用成员变量:父类名.成员变量
    • 使用成员方法:父类名.成员方法(self)
  2. 使用super()调用父类成员:
    • 使用成员变量:super().成员变量
    • 使用成员方法:super().成员方法()
    • 通过super父类().方法或属性直接调用,且调用方法时不用加上self
class Phone:
    IMEI = None
    product = "苹果手机"

    def call_by_5g(self):
        print("父类5g通话")


class Phone2024(Phone):
    IMEI = "10001"
    product = "华为手机"

    def call_by_5g(self):
        print(f"父类的产品是:{Phone.product}")  # 1.调用父类属性
        Phone.call_by_5g(self)  # 2.调用父类属性
        # print("子类重写5g通话,视频通话")
        print(f"父类的产品是:{super().product}")  # 3.调用父类属性
        super(Phone2024, self).call_by_5g()  # 4.调用父类方法 或 super().call_by_5g()


phone = Phone2024()
phone.call_by_5g()

小结:

  1. 复写表示:对父类的成员属性或成员方法进行重写定义;
  2. 复写的语法:在子类中重新实现同名成员方法或成员属性即可;

3. 变量的类型注解:给变量标识变量类型

3.1 为什么需要类型注解

(就是开发工具无法识别到自定义的方法中的参数类型,在编写代码时无方法提示参数类型)还有可以清晰明了变量的类型;

前者有解释说要传递一个int类型的参数;

而后者只告诉了我们传递的参数名是data,并不知道是什么类型的参数;

3.2 类型注解
  • Python在3.5版本的时候引入了类型注解,以方便静态类型检查工具,IDE等第三方工具;

  • 类型注解:在代码中涉及数据交互的地方,提供数据类型的注解(显式的说明);

  • 主要功能:

    帮助第三方IDE工具(如PyCharm,IDEA)对代码进行类型推断,协助做代码提示;

    帮助开发者自身对变量进行类型注释;

  • 支持:

    变量的类型注解;

    函数(方法)形参列表和返回值的类型注解;

3.3 类型注解的语法

1. 基础类型注解:var_1: int = 10

  • 基础类型注解一般可以不写,写的情况是无法一眼看出变量类型的时候写入;
# 基础数据类型注解
var_1: int = 10
var_2: float = 3.1415926
var_3: bool = True
var_4: str = "zhangSan"


# 类对象类型注解
class Student:
    pass


stu: Student = Student()

2. 容器类型注解:my_tuple: tuple = (1, 2, 3) || my_tuple: tuple[str, int, boll] = ("xxx", 88, True) 比较常用

  • 可以对容器类型继续标记类型,以及对容器中内部进行标记类型;
    1. 基础容器类型注解;
    2. 容器类型详细注解;
# 基础容器类型注解
my_listL: list = [1, 2, 3]
my_tuple: tuple = (1, 2, 3)
my_set: set = {1, 2, 3}
my_dict: dict = {"age": 3}
my_str: str = "zhangSan"

# 容器类型详细注解
my_list: list[int] = [1, 2, 3]
my_tuple1: tuple[str, int, bool] = ("zhangSan", 88, True)  # 元组类型设置类型详细注解,需要将每一个元素都标记出来
my_set1: set[int] = {1, 2, 3}
my_dict1: dict[str, int] = {"age": 120}  # 字典类型设置类型详细注解,需要2个类型,第一个是key第二个是value

3. 注释标记变量类型var_1 = random.randint(1, 10) # type: int

除了使用变量:类型,这种语法做注解外,也可以在注释中进行类型注解;

语法:# type:类型

3.4 类型的注解限制
  • 类型标记错误,开发工具中都会有提示;
  • 预期的是int类型,但是具体得到的类型是str类型;

  • 类型注解主要功能在于:
    • 帮助第三方IDE工具(如PyCharm)对代码进行类型推断,协助做代码提示;
    • 帮助开发者自身对变量进行类型注释(备注);
  • 并不会真正的对类型做验证和判断,也就是类型注解仅仅是提示,并不能决定程序的走向;

4. 函数和方法类型的注解:给函数和方法标识数据类型

  • 在函数中定义的类型,只是提示性的,并非决定性的,就是执行过程中,类型输入不正确也不会报错;
4.1 函数(方法)的类型注解-形参注解
def func(data):
    pass


def func1(name: str, score: tuple):  # 形参名:类型,形参名:类型,...
    pass


func(data=1)
func1("zhangSan", (80, 100, 90))

4.2 函数(方法)的类型注解-返回值注解

函数的型参可以加类型,其函数返回值也可以添加类型注解;

语法:

def 函数方法名(形参: 类型, ..., 形参: 类型) -> 返回值类型:
    pass
def func(data: list) -> list:
    return data


func()

5. Union联合类型注解:给变量、函数和方法、形参和返回值标识联合类型注解

5.1 Union概念

使用Union解决无法确认的类型情况:

如果是list中,出现不规则的数据类型,那么无法之前的类型注解去确认数据的类型,而是可以采用Union来表示,list中包含的数据类型:my_list: list[Union[str, int]] = [1, 2, "xxx", "xxx"] ;

字典的key: value也是一样,可以用Union注解来标识,这个key或value中可以包含什么样的数据类型;

5.2 Union使用
  • 使用方式:from typing import Union
  • 使用:Union[类型, ..., 类型]

其他类型也可以使用Union类型注解,函数(方法)、形参和返回值注解中都可以;

# 使用Union类型,必须导入Union包
from typing import Union

# 作用在基础类型上
my_list: list[Union[int, str]] = [1, 2, "zhangSan", "lisi"]


# 作用在函数上
def func(data: Union[int, str]) -> Union[int, str]:
    pass


func(data=1)
func(data="zhangSan")

6. 多态

6.1 多态使用
  • 多态抽象概念:一个设计方法,里面是空的,但是定义了标准的格式,需要有具体的实现,而实现可以有多种,可以随意调用某个实现类,这就是多态;
  • 或者说:多态,就是多种状态,即完成某个行为时,使用不同的对象会得到不同的状态;
  • 多态常作用在继承关系上;
  • 比如:
    • 函数(方法)形参声明接收父类对象;
    • 实际传入父类的子类对象进行工作;
  • 即:
    • 以父类做定义声明;
    • 以子类做实际工作;
    • 用以获得同一行为,不同状态;
class Animal:
    def speak(self):
        pass


class Dog(Animal):
    def speak(self):
        print("wang wang wang")


class Cat(Animal):
    def speak(self):
        print("miao miao miao")


def make_noise(animal: Animal):  # 多态的使用
    animal.speak()


dog = Dog()
cat = Cat()
make_noise(dog)
make_noise(cat)

6.2 抽象类(接口)

1. 抽象类(接口概念)配合多态使用:

  • 这种设计的含义是:

    • 父类用来确定有哪些方法;
    • 具体的方法实现,由子类自行决定;
  • 抽象类(也可以称为接口)

  • 抽象类:含有抽象方法的类称之为抽象类,无具体实现;

  • 抽象方法:方法体是空实现的(pass)称之为抽象方法;

  • 概念:抽象类就好比定义一个标准;包含了一些抽象方法,要求子类必须实现;

  • 一般抽象类会配合多态实现:

    • 抽象的父类设计(设计标准)
    • 具体的子类实现(实现标准)

2. 案例:

        大致就是,定义一个抽象类(接口):只有方法,没有实现,而子类去实现抽象类,子类继承接口,去实现接口中的方法,而定义一个单独的调用类,将接口作为新参传递,调用类具体的使用,是由继承接口的是实现类作为参数传递,去使用;传递的是哪个实现类,那么就可以用哪个实现的方法,从而统一方法;        

小结:

  1. 什么是多态:
    • 同一行为,不同对象获得不同的状态;
  2. 什么是抽象类(接口):
    • 包含抽象方法的类,称之为抽象类;
    • 抽象方法:没有具体的实现方法(pass);
  3. 抽象类的作用:
    • 多用于做顶层设计(设计标准),以便子类做具体实现;
    • 对子类有一种软性约束,要求子类必须复写(实现)父类的一些方法;
    • 一般配合多态使用,获得不同的工作状态;

7. 案例:使用面向对象概念实现

  • 案例:使用面向对象概念实现
    1. 使用面向对象思想完成数据读取和处理;
    2. 基于面向对象思想重新认知第三方库使用(PyEcharts);
7.1 数据分析案例-文件读取
  • 实现第一步和第二步;
  • 需求:对2份数据文件,进行计算每日的销售额并以柱状图表的形式进行展示;

1. 数据内容:

2024年6月数据:

2024-06-01,0184042c5eff11eda948fa163e3ae10e,1980,浙江省
2024-06-01,01849af15eff11eda948fa163e3ae10e,1980,安徽省
2024-06-01,01849bf75eff11eda948fa163e3ae10e,1980,河南省
2024-06-01,01849c4f5eff11eda948fa163e3ae10e,1980,四川省

2024年7月销售数据JSON:

{"date":"2024-07-01","order_id":"0184042c5eff11eda948fa163e3ae10e","money":1805,"province":"浙江省"}
{"date":"2024-07-01","order_id":"01849af15eff11eda948fa163e3ae10e","money":1999,"province":"安徽省"}
{"date":"2024-07-01","order_id":"01849bf75eff11eda948fa163e3ae10e","money":2025,"province":"河南省"}
{"date":"2024-07-01","order_id":"01849c4f5eff11eda948fa163e3ae10e","money":1685,"province":"四川省"}
{"date":"2024-07-02","order_id":"0184042c5eff11eda948fa163e3ae10e","money":1800,"province":"浙江省"}
{"date":"2024-07-02","order_id":"01849af15eff11eda948fa163e3ae10e","money":1586,"province":"安徽省"}
{"date":"2024-07-02","order_id":"01849bf75eff11eda948fa163e3ae10e","money":1950,"province":"河南省"}
{"date":"2024-07-02","order_id":"01849c4f5eff11eda948fa163e3ae10e","money":2600,"province":"四川省"}
  1. 1月份数据是普通文本,使用逗号分割数据记录,从前到后分别是(日期,订单id,销售额,销售省份)
  2. 2月份份数据是JSON数据,同样包含(日期,订单id,销售额,销售省份)

2. 需求分析:

  1. 读取数据:设计FileReader类;
  2. 封装数据对象:设计数据封装类;
  3. 计算数据对象:对对象进行逻辑计算;
  4. pyecharts绘图:以面向对象思想重新认知pyecharts;

3. 具体实现:

第一步:定义数据分析类(完成数据封装)

"""
数据定义类
"""


class Record:
    def __init__(self, date, order_id, money, province):
        self.date = date  # 订单日期
        self.order_id = order_id  # 订单ID
        self.money = money  # 订单金额
        self.province = province  # 销售省份

    def __str__(self) -> str:
        return f"{self.date}, {self.order_id}, {self.money}, {self.province}"

第二步:文件读取抽象类,确定需要的功能,比如读取字符串,读取JSON

  • 提示:
  • 在该代码上加上类型转换;
"""
文件相关的类定义
"""
import json

from data_define import Record


# 定义抽象类做顶层设计,确认功能需求实现
class FileReader:
    def read_data(self) -> list[Record]:
        """ 读取文件的数据,读到的每一条转换为Record对象,并封装为list返回 """
        pass


# 实现类
class TextFileReader(FileReader):
    def __init__(self, path):
        self.path = path  # 定义成员变量记录文件路径

    # 复写抽象方法
    def read_data(self) -> list[Record]:
        f = open(self.path, 'r', encoding="UTF-8")
        record_list: list[Record] = []
        for line in f.readlines():
            line = line.strip()  # 消除读取到每一行的数据中的\n
            data_list = line.split(",")  # 根据逗号,分割
            record = Record(data_list[0], data_list[1],
                            data_list[2], data_list[3])  # 将读取文件的数据赋值给对象
            record_list.append(record)
            print(record)
        f.close()
        return record_list


# 实现类
class JsonFileReader(FileReader):
    def __init__(self, path):
        self.path = path  # 定义成员变量记录文件路径

    def read_data(self) -> list[Record]:
        f = open(self.path, 'r', encoding="UTF-8")
        record_list: list[Record] = []
        for line in f.readlines():
            data_dict = json.loads(line)
            record = Record(data_dict["data"], data_dict["order_id"],
                            data_dict["money"], data_dict["province"])  # 将读取文件的数据赋值给对象
            record_list.append(record)
        f.close()
        return record_list


if __name__ == '__main__':
    text_file_reader = TextFileReader("../file/2024年6月数据")
    data = text_file_reader.read_data()
    print(data)

内容调用输出:

        定义实现类对象,并且传递文件读取路径,而实现类返回的是list对象,对象中去循环打印文件读取出来的list,并有了str魔术方法,以字符串构建;

if __name__ == '__main__':
    text_file_reader = TextFileReader("../file/2024年6月数据")
    json_file_reader = JsonFileReader("../file/2024年7月销售数据JSON")
    data = text_file_reader.read_data()
    data1 = json_file_reader.read_data()
    for li in data:
        print(li)
    for li in data1:
        print(li)

7.2 数据分析案例-数据计算

提示:list合并:list3 = list01 + list02

数据计算:

from file_define import FileReader, TextFileReader, JsonFileReader
from data_define import Record

text_file_reader = TextFileReader("../file/2024年6月数据")
json_file_reader = JsonFileReader("../file/2024年7月销售数据JSON")

jan_data: list[Record] = text_file_reader.read_data()
feb_data: list[Record] = json_file_reader.read_data()

# 将2各月份的数据合并
all_data: list[Record] = jan_data + feb_data

# 开始进行数据计算
data_dict = {}
for record in all_data:
    if record.date in data_dict.keys():
        # 当前日志已有记录,与旧记录累加
        data_dict[record.date] += record.money
    else:
        data_dict[record.date] = record.money
print(data_dict)

7.3 数据分析案例-可视化开发
from file_define import FileReader, TextFileReader, JsonFileReader
from data_define import Record
# 可视化图标开发
from pyecharts.options import InitOpts, LabelOpts, TitleOpts
from pyecharts.charts import Timeline, Bar
from pyecharts.globals import ThemeType

text_file_reader = TextFileReader("../file/2024年6月数据")
json_file_reader = JsonFileReader("../file/2024年7月销售数据JSON")

jan_data: list[Record] = text_file_reader.read_data()
feb_data: list[Record] = json_file_reader.read_data()

# 将2各月份的数据合并
all_data: list[Record] = jan_data + feb_data

# 开始进行数据计算
data_dict = {}
for record in all_data:
    if record.date in data_dict.keys():
        # 当前日志已有记录,与旧记录累加
        data_dict[record.date] += record.money
    else:
        data_dict[record.date] = record.money
# print(data_dict)


bar = Bar(init_opts=InitOpts(theme=ThemeType.LIGHT))
bar.add_xaxis(list(data_dict.keys()))
bar.add_yaxis("销售额", list(data_dict.values()), label_opts=LabelOpts(is_show=False))
bar.set_global_opts(
    title_opts=TitleOpts(title="每日销售额")
)
bar.render("每日销售额柱状图.html")

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值