Python第十三章 面向对象

第十三章 面向对象

13.1 类的定义#

class 类名称:

	类的属性

	类的行为
  • class是关键字,表示要定义类了
  • 类的属性,即定义在类中的变量(成员变量)
  • 类的行为,即定义在类中的函数(成员方法)

创建类的语法

对象 = 类名称()

13.1.1 成员变量#

class Student:

    name = None

    age = None

    sex = None





student = Student()

student.name = "jack"

student.age = 18

student.sex = "male"



print(student.name)  # jack

print(student.age)  # 18

print(student.sex)  # male

13.1.2 成员方法#

class Student:

    name = None



    def say_hi(self):

        print(f"hi, my name is {self.name}")





student = Student()

student.name = "jack"



student.say_hi()  # hi, my name is jack

在类中定义成员方法和定义函数基本一致,但仍有细微区别:

def 方法名(self, param1, param2, ..., paramN):

	方法体

可以看到,在方法定义的参数列表中,有一个:self关键字

self关键字是成员方法定义的时候,必须填写的

  • 它用来表示类对象自身的意思
  • 当我们使用类对象调用方法的是,self会自动被python传入(也就是调用方法的时候不需要传入self,传入后面的参数即可)
  • 在方法内部,想要访问类的成员变量,必须使用self

比如

def say_hi(self):

    print(f"hi, my name is {self.name}")

调用成员方法的时候忽略self参数

student.say_hi()

13.2 特殊成员方法#

下面几种方法是每个类的内置方法,也称为魔术方法

方法 功能
__init__ 构造方法,可用于创建类对象的时候设置初始化行为
__str__ 用于实现类对象转字符串的行为
__lt__ 用于2个类对象进行小于或大于比较
__le__ 用于2个类对象进行小于等于或大于等于比较
__eq__ 用于2个类对象进行相等比较

【说明】实际上的魔术方法有很多,这里只学习最常用的几个

13.2.1 构造方法#

Python类可以使用:__init__()方法,称之为构造方法。

class Student:

    name = None

    age = None

    sex = None





student = Student()

student.name = "jack"

student.age = 18

student.sex = "male"

上述代码太复杂了,可以使用构造方法__init__()

可以实现:

  • 在创建类对象(构造类)的时候,会自动执行。
  • 在创建类对象(构造类)的时候,将传入参数自动传递给__init__方法使用。
class Student:

    name = None

    age = None

    sex = None



    def __init__(self, name, age, sex):

        self.name = name

        self.age = age

        self.sex = sex

        print("constructor method")



    def say_hi(self):

        print(f"hi, my name is {self.name}, my age is {self.age}, my sex is {self.sex}")





student = Student("jack", 18, "female")

print(student.name)  # jack

print(student.age)  # 18

print(student.sex)  # female

student.say_hi()  # hi, my name is jack, my age is 18, my sex is female

【注意】不要将init写成int

在构造函数里面赋值的变量可以不必在类中声明了,在构造函数中直接 定义 + 声明

class Student:

    # 成员变量不用写

    # name = None

    # age = None

    # sex = None



    def __init__(self, name, age, sex):

        # 构造方法里面声明 + 初始化

        self.name = name

        self.age = age

        self.sex = sex

        print("constructor method")



    def say_hi(self):

        print(f"hi, my name is {self.name}, my age is {self.age}, my sex is {self.sex}")





student = Student("jack", 18, "female")

print(student.name)  # jack

print(student.age)  # 18

print(student.sex)  # female

student.say_hi()  # hi, my name is jack, my age is 18, my sex is female

如果显示定义了构造函数,那么默认的无参构造函数就没有了

student2 = Student() # 错误:TypeError: Student.__init__() missing 3 required positional arguments: 'name', 'age', and 'sex'

13.2.2 字符串方法#

默认情况下:当类对象需要被转换成字符串的时候,会被转换成内存地址

student = Student()

print(student)  # <__main__.Student object at 0x00000150609B7010>

可以使用__str__方法设置转换的字符串

class Student:



    def __init__(self, name, age, sex):

        self.name = name

        self.age = age

        self.sex = sex



    def __str__(self):

        return f"name:{self.name}, age:{self.age}, sex:{self.sex}"



student = Student("jack", 18, "male")

print(student)  # name:jack, age:18, sex:male

13.2.3 小于比较方法#

默认情况下,对象是不允许比较的

student1 = Student("jack", 18, "male")

student2 = Student("mary", 19, "female")

print(student1 < student2)  # TypeError: '<' not supported between instances of 'Student' and 'Student'

使用__lt__来比较

class Student:

    

    def __init__(self, name, age, sex):

        self.name = name

        self.age = age

        self.sex = sex



    # 定义了<的比较规则

    def __lt__(self, other):

        return self.age < other.age



student1 = Student("jack", 18, "male")

student2 = Student("mary", 19, "female")

print(student1 < student2)  # True

print(student1 > student2)  # False

【说明】比较大于符号的魔术方法是:gt__不过,实现了__lt,__gt__就没必要实现了

13.2.4 ≤、=比较方法#

≤对应的是__le__,=对应的是__eq__

class Student:



    def __init__(self, name, age, sex):

        self.name = name

        self.age = age

        self.sex = sex

        print("constructor method")



    def __le__(self, other):

        return self.age <= other.age



    def __eq__(self, other):

        return self.age == other.age



student1 = Student("jack", 18, "male")

student2 = Student("mary", 19, "female")

print(student1 <= student2)  # True

print(student1 == student2)  # False

13.3 面向对象三大特性#

面向对象编程,是许多编程语言都支持的一种编程思想。简单理解是:基于模板(类)去创建实体(对象),使用对象完成功能开发。

面向对象包含3大主要特性:封装、继承、多态

13.3.1 封装#

封装表示的是,将现实世界事物的:属性、行为,封装到类中,描述为:成员变量、成员方法,从而完成程序对现实世界事物的描述

image-20230927210055389

(1) 私有成员#

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

类中提供了私有成员的形式来支持。

  1. 私有成员变量
  2. 私有成员方法

【说明】私有成员包括变量和方法

定义私有成员的方式非常简单,只需要:

  1. 私有成员变量:变量名以__开头(2个下划线)
  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 = 13  # 不能访问私有变量

print(phone.__current_voltage)

2、在成员方法内,可以访问私有方法

class Phone:

    IMEI = None  # 序列号

    producer = None  # 厂商



    __current_voltage = 10  # 当前电压



    def call_by_5g(self):

        if self.__current_voltage >= 1:

            self.__keep_single_core()

        print("5g通话已开始")



    def __keep_single_core(self):

        print("让CPU一但和模式运行以节省电量")





phone = Phone()

phone.call_by_5g()  # 5g通话已开始

13.3.2 继承#

(1) 继承语法#

继承的语法

class 子类名(父类名):

	类内容体

继承分为:单继承和多继承

继承表示:将从父类那里继承(复制)来成员变量和成员方法(不含私有)(私有的成员不会继承)


1、单继承

class Father:

    name = "father"



    def father_name(self):

        print(f"father_name is {self.name}")





class Son(Father):

    name = "son"



    def son_name(self):

        print(f"son_name is {self.name}")





son = Son()

son.father_name()  # father_name is son

son.son_name()  # son_name is son

2、多继承

class 子类名(父类1, 父类2, 父类3... 父类N):

	类内容体

下面是一个案例

image-20230927213536057

class Phone:

    IMEI = None  # 序列号

    producer = None  # 厂商



    def call_by_5g(self):

        print("5g通信")





class NFCReader:

    nfc_type = "第五代"

    producer = "HM"



    def read_card(self):

        print("读取NFC卡")



    def write_card(self):

        print("写入NFC卡")





class RemoteControl:

    rc_type = "红外遥控"



    def control(self):

        print("红外遥控开启")





class MyPhone(Phone, NFCReader, RemoteControl):

    pass  





xiaomi = MyPhone()

xiaomi.call_by_5g()  # 5g通信

xiaomi.read_card()  # 读取NFC卡

xiaomi.write_card()  # 写入NFC卡

xiaomi.control()  # 红外遥控开启

【说明】pass是占位语句,用来保证函数(方法)或类定义的完整性,表示无内容,空的意思

3、多继承的变量覆盖问题

多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级。也就是最左边的优先级最高

如上面的例子,Phone和NFCReader都有producer的变量,最后只有是最左边的Phone的变量有效

print(xiaomi.producer) # None

(2) 重写父类成员#

class Father:

    var = "father"



    def func(self):

        print("father method")





class Son(Father):

    var = "son"



    # 覆盖了父类方法

    def func(self):

        print("son method")





son = Son()

son.func()  # son method

print(son.var)  # son

(3) 调用父类同名成员#

一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员

如果需要使用被复写的父类的成员,需要特殊的调用方式:

方式1:调用父类成员

  • 使用成员变量:父类名.成员变量
  • 使用成员方法:父类名.成员方法(self)

方式2:使用super()调用父类成员

  • 使用成员变量:super().成员变量
  • 使用成员方法:super().成员方法()

【说明】只能在子类内调用父类的同名成员(不能在类外)。子类的类对象直接调用会调用子类复写的成员

1、父类名调用

class Father:

    var = "father"



    def func(self):

        print("father method")





class Son(Father):

    var = "son"



    # 覆盖了父类方法

    def func(self):

        # print("son method")

        Father.func(self)  # father method,这里得传入self

        print(Father.var)  # father





son = Son()

son.func() 

2、super()调用

class Father:

    var = "father"



    def func(self):

        print("father method")





class Son(Father):

    var = "son"



    # 覆盖了父类方法

    def func(self):

        # print("son method")

        super().func()  # father method,不用传入self参数

        print(super().var)  # father





son = Son()

son.func()  # son method

13.3.3 多态#

多态,指的是:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态

class Animal:

    def speak(self):

        pass





class Dog(Animal):

    def speak(self):

        print("wangwang")





class Cat(Animal):

    def speak(self):

        print("miaomiao")





def make_noise(animal: Animal):

    animal.speak()





dog = Dog()

cat = Cat()



make_noise(dog)  # wangwang

make_noise(cat)  # miaomiao

多态常作用在继承关系上,比如

  • 函数(方法)形参声明接收父类对象
  • 实际传入父类的子类对象进行工作

即:以父类做定义声明、以子类做实际工作,用以获得同一行为, 不同状态

上述例子可以看到父类Animal的speak方法是空实现

class Animal:

    def speak(self):

        pass

这种设计的含义是:

  1. 父类用来确定有哪些方法
  2. 具体的方法实现,由子类自行决定

这种写法,就叫做抽象类(也可以称之为接口)

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

【说明】含有pass的方法是抽象方法,含有抽象方法的类就是抽象类

抽象类的作用:多用于做顶层设计(设计标准),以便子类做具体实现;也是对子类的一种软性约束,要求子类必须复写(实现)父类的一些方法;并配合多态使用,获得不同的工作状态。

【说明】抽象类(接口)是很能体现多态的一点,父类都设置为抽象方法,都交由子类实现

class AC:

    def cool_wind(self):

        """制冷"""

        pass



    def hot_wind(self):

        """制热"""

        pass



    def swing_l_r(self):

        """左右摇摆"""

        pass





class Midea_AC(AC):

    def cool_wind(self):

        print("美的空调制冷")



    def hot_wind(self):

        print("美的空调制热")



    def swing_l_r(self):

        print("美的空调左右摇摆")





class GREE_AC(AC):

    def cool_wind(self):

        print("格力空调变频省电制冷")



    def hot_wind(self):

        print("格力空调变频省电制热")



    def swing_l_r(self):

        print("格力空调静音左右摆风")





def make_cool(ac: AC):

    ac.cool_wind()





midea_ac = Midea_AC()

gree_ac = GREE_AC()



make_cool(midea_ac)  # 美的空调制冷

make_cool(gree_ac)  # 格力空调变频省电制冷

13.4 面向对象练习#

【说明】本例题重在讲解Python中的面向对象思维

某公司,有2份数据文件,现需要对其进行分析处理,计算每日的销售总额(所有地区的销售额加起来)并以柱状图表的形式进行展示。

两份文件如下

# 2011-1-data.txt, 文本文件,中间用,隔开

2011-01-01,4b34218c-9f37-4e66-b33e-327ecd5fb897,1689,湖南省

2011-01-01,4b34218c-9f37-4e66-b33e-327ecd5fb897,1689,湖南省

2011-01-01,5b6a6417-9a16-4243-9704-255719074bff,2353,河北省

2011-01-01,ae240260-68a9-4e59-b4c9-206be4c08a8d,2565,湖北省

2011-01-01,c833e851-880f-4e05-9de5-b547f5ffc5e1,2877,山东省

...

2011-01-02,4681b5fe-b1e9-468b-a6f5-9f98f5daa841,1931,山西省

2011-01-02,d1a62946-f470-4782-9461-fde425c759e2,2416,江苏省

2011-01-02,6d49171f-4522-4561-a191-4514e3d2a5d3,1171,江苏省

2011-01-02,a92ca744-b61d-44dc-ad03-7d99842e2b53,170,广西省

2011-01-02,25b46bc5-e686-4de1-b382-ef105261752f,2517,浙江省

2011-01-03,a59fcc9f-fd03-4232-9da7-6ef73ceb4aba,2696,贵州省

2011-01-03,0cdb43a5-3a61-4ae9-b67a-f2216f2f0f79,1890,云南省

2011-01-03,0fec45b5-6f3d-4906-a057-f5dc87bb6891,2231,贵州省

2011-01-03,6c36b4f8-b4d3-4ca0-94dd-d0863e695e40,914,山东省

2011-01-03,cdec53ab-8c14-4d56-afc8-6512bd82f5d3,1021,河北省

2011-01-03,24c1f00b-04b9-474b-a2f5-a893c47667f2,2317,河南省

【说明】下面的文件每行是一个JSON文件,但是整体不能解析,因为外面没有[],每行后面也没有,如果作为JSON转换的话,需要一行一行读取

# 2011-2-data.txt 每行都是一个JSON文件

{"date": "2011-02-01", "order_id": "caf99222-53d6-427b-925d-3187fc71a86a", "money": 1805, "province": "江西省"}

{"date": "2011-02-01", "order_id": "3dea6f83-a9b2-4197-ba9f-2b25704c530b", "money": 2547, "province": "广东省"}

{"date": "2011-02-01", "order_id": "93cf7a56-3f90-4df9-af76-de7233c1dddb", "money": 1216, "province": "福建省"}

{"date": "2011-02-01", "order_id": "0042323a-e555-4d64-a70b-81380ca3aae7", "money": 1193, "province": "四川省"}

{"date": "2011-02-01", "order_id": "0fbe1745-ac65-48f4-985b-b71875fcfbf7", "money": 230, "province": "云南省"}

{"date": "2011-02-01", "order_id": "6ee5af28-f6ce-42e3-96b6-96f3844c5fda", "money": 2770, "province": "江西省"}

{"date": "2011-02-01", "order_id": "2f629b33-62a5-44c1-8a0f-b983e811fb40", "money": 2291, "province": "福建省"}

{"date": "2011-02-01", "order_id": "773d1259-f81d-413d-81cb-b5f7c0d8f97c", "money": 2453, "province": "河南省"}

...

{"date": "2011-02-02", "order_id": "1e7189bb-9369-4bae-9d70-fe349ca75997", "money": 2127, "province": "安徽省"}

{"date": "2011-02-02", "order_id": "99fc2f6c-f373-42d0-ac27-0203dbad385b", "money": 396, "province": "江西省"}

{"date": "2011-02-02", "order_id": "9e0eeebd-4776-4023-bd3c-a4c449f1702f", "money": 577, "province": "广西省"}

{"date": "2011-02-02", "order_id": "0a8e7c56-cd8a-4649-a6a6-327578bc4793", "money": 2272, "province": "江苏省"}

{"date": "2011-02-02", "order_id": "bfb831c9-859b-4bef-a762-c466c32e7e8f", "money": 220, "province": "广西省"}

{"date": "2011-02-03", "order_id": "3eff3650-9999-4278-8b40-8ba5217888a1", "money": 366, "province": "云南省"}

{"date": "2011-02-03", "order_id": "ebb139e5-4037-4019-b4a6-2e074c651d1f", "money": 2555, "province": "广东省"}

{"date": "2011-02-03", "order_id": "08c78f66-46c2-46e6-b155-bbd259c354d4", "money": 1955, "province": "福建省"}

{"date": "2011-02-03", "order_id": "315ae98c-c0fa-4e08-ae29-5c8c8abcdb7a", "money": 1430, "province": "山东省"}

【感悟】面向对象过程能够将过程变得更具有模块化,很好地分割业务

1、面向过程思路

import json



from pyecharts.charts import Bar



data_2011 = open("D:/2011-1-data.txt", "r", encoding="UTF-8")

# data_2011.close()

# print(data_2011.read())

# {date_sum}的列表

date_money: dict[str, int] = {}

#

for line in data_2011.readlines():

    line_split = line.split(",")

    date = line_split[0]

    money = int(line_split[2])

    try:

        date_money[date] += money

    except KeyError:

        date_money[date] = money



data_2011.close()





# 第一个数据结束了

# 然后是第二轮数据

data_2022 = open("D:/2011-2-data.txt", "r", encoding="UTF-8")

# print(data_2022.read())

# 这里前后要加上[],这里面每行都要加上,来进行,那就每行都处理吧?

for line in data_2022.readlines():

    line_json = json.loads(line)

    date = line_json["date"]

    money = line_json["money"]

    try:

        date_money[date] += money

    except KeyError:

        date_money[date] = money

data_2022.close()



# for date in date_money.keys():

#     print(date, date_money[date])



# 然后按照时间排序

date_list = sorted(date_money.keys())

print(date_list)



# 然后加入到list中

money_list = []

for date in date_list:

    money_list.append(date_money[date])



print(money_list)



# 然后加入到柱状图中

bar = Bar()

# 添加x轴数据

bar.add_xaxis(date_list)

bar.add_yaxis("money", money_list)

bar.render("1-2月销售额.html")

2、面向对象思路

首先将每一行当做一个Record对象

# Record.py

class Record:



    def __init__(self, date: str, order_id: str, money: int, province: str):

        # print("constructor:", date, order_id, money, province)

        self.date = date

        # 这里不能加逗号

        self.order_id = order_id # ,

        self.money = money

        self.province = province



    def __str__(self):

        return f"{self.date}, {self.order_id}, {self.money}, {self.province}"

然后将文件读取也方入一个类

# FileReader.py

import json



from Record import Record





class FileReader:

    def __init__(self, path):

        """constructor"""

        self.path = path



    def parse_file(self) -> list[Record]:

        pass





class TextFileReader(FileReader):



    def parse_file(self) -> list[Record]:

        """

        解析path对应的文件,然后将每行解析为Record,最后作为列表返回

        :return:

        """

        file = open(self.path, "r", encoding="UTF-8")

        record_list = []

        for line in file.readlines():

            split_line = line.strip().split(",")

            # 下面的可以直接放到构造器里面的

            date = split_line[0]

            order_id = split_line[1]

            money = int(split_line[2])

            province = split_line[3]

            record = Record(date, order_id, money, province)

            record_list.append(record)



        file.close()

        return record_list



class JsonFileReader(FileReader):

    def parse_file(self) -> list[Record]:

        """

        解析path对应的文件,然后将每行解析为Record,最后作为列表返回

        :return:

        """

        file = open(self.path, "r", encoding="UTF-8")

        record_list = []

        for line in file.readlines():

            record_json = json.loads(line)

            # 下面的可以直接放到构造器里面的

            date = record_json["date"]

            order_id = record_json["order_id"]

            money = int(record_json["money"])

            province = record_json["province"]

            record = Record(date, order_id, money, province)

            record_list.append(record)



        file.close()

        return record_list





if __name__ == '__main__':

    # 测试TextFileReader

    text_file_reader = TextFileReader("D:/2011-1-data.txt")

    # for record in text_file_reader.parse_file():

    #     print(record.date, record.order_id, record.money, record.province)

    # text_file_reader.parse_file()

    

    # 测试JsonFileReader

    json_file_reader = JsonFileReader("D:/2011-2-data.txt")

    for record in json_file_reader.parse_file():

        print(record)

最后进行读取,构建图表

from pyecharts.charts import Bar

from FileReader import TextFileReader, JsonFileReader



# 读取两个文件

text_file_reader = TextFileReader("D:/2011-1-data.txt")

json_file_reader = JsonFileReader("D:/2011-2-data.txt")



# 两个文件的列表加起来,就是最终的记录列表

record_list = text_file_reader.parse_file() + json_file_reader.parse_file()



# 然后将所有的数据聚合在一起,构成dict[date, money]

date_money = {}



for record in record_list:

    try:

        date_money[record.date] += record.money

    except KeyError:

        date_money[record.date] = record.money



# 然后排序

date_list = sorted(date_money.keys())



# 然后将钱按时间取出放入列表中

money_list = []

for date in date_list:

    money_list.append(date_money[date])



# 构造表格

bar = Bar()



# 添加横坐标

bar.add_xaxis(date_list)

bar.add_yaxis("money", money_list)



bar.render("1-2月每日销售额.html")

image-20230929220947156

posted @   Crispy·Candy  阅读(26)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
主题色彩