8. Python 面向对象

1. 初识对象

设计类:

class Student:
	name = None

创建对象:

stu_1 = Student()
stu_2 = Student()

对象属性赋值:

stu_1.name="tom"
stu_2.name ="jerry"

【例】

class Student:
    name = None
    age = None
    gender = None
    nationality = None

stu = Student()

stu.name = "小明"
stu.age = 11
stu.gender = "男"
stu.nationality = "中国"

print(stu.name)
print(stu.age)
print(stu.gender)
print(stu.nationality)

2. 成员方法

上一节介绍了如何封装一个类,并基于类创建对象来使用。现在来看类更详细的使用语法:

class 类名称:
	成员变量
	成员方法

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

def 方法名(self,形参1,...,形参N):
	方法体

在定义方法时,形参列表中的 self 关键字必须填写,用于代表对象自身。
当使用对象调用方法时,self 会被自动传入,即传参时忽略 self,当它不存在。
在方法内部访问成员变量,必须使用 self。(Java 中只有方法内的变量与成员变量同名时,才必须使用 this)

创建对象的语法:

对象 = 类名()

【例】

class Student:
    name = None
    age = None
    def say_hi(self):
        print(f"hi, 我是{self.name}")
    def say_hello(self, msg):
        print(f"hello, 我是{self.name}, {msg}")

stu1 = Student()
stu1.name = "tom"
stu1.say_hi()
stu1.say_hello("希望大家多多关照")

stu2= Student()
stu2.name = "jerry"
stu2.say_hi()
stu2.say_hello("很高兴认识大家")

输出结果:

hi, 我是tom
hello, 我是tom, 希望大家多多关照
hi, 我是jerry
hello, 我是jerry, 很高兴认识大家

3. 类和对象

类相当于模板,对象就是根据类这个模板生产出来的具体实体。

class Clock:
    id = None
    price = None
    def ring(self): # 响铃
        import winsound
        # 参数1:频率;参数2:持续时间
        winsound.Beep(2000,3000)

clock1 = Clock()
clock1.id = 111
clock1.price = 19.8
print(f"闹钟{clock1.id}的价格是{clock1.price}")
clock1.ring()

clock2 = Clock()
clock2.id = 222
clock2.price = 32.3
print(f"闹钟{clock2.id}的价格是{clock2.price}")
clock2.ring()

输出结果(输出的时候还会有响铃,声音略微感人~~):

闹钟111的价格是19.8
闹钟222的价格是32.3

4. 构造方法

看一段代码:

class Student:
    name = None 
    age = None
    tel = None
    
student1 = Student()
student1.name ="tom"
student1.age = 20
student1.tel ="18012340000"

student2 = Student()
student2.name="jerry"
student2.age = 30
student2.tel ="19017840900"

在上面代码中,为对象的属性赋值需要依次进行,略显繁琐。可以使用构造方法简化。

可以使用构造方法__init__() 初始化对象:在创建对象时,会自动执行 __init__() ,并将传入参数自动传递给__init__()方法使用。

构造方法使用1:

class Student:
	# 这三行定义可以不用写,见“构造方法使用2”
	# 如果不写的话,__init__()构造方法中的self.name self.age self.gender 三条语句就既有定义作用,也有赋值作用
    name = None
    age = None
    gender = None
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
        print("创建了Student对象")

stu = Student("tom", 14, "男")
print(stu.name)
print(stu.age)
print(stu.gender)

输出结果:

创建了Student对象
tom
14

构造方法使用2:

class Student:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
        print("创建了Student对象")

stu = Student("tom", 14, "男")
print(stu.name)
print(stu.age)
print(stu.gender)

输出结果:

创建了Student对象
tom
14

5. 内置方法 / 魔术方法

上文学习的__init__()构造方法,是 Python 类内置的方法之一。
内置的类方法,各自有各自特殊的功能,也称为:魔术方法。主要介绍下面几个魔术方法:
在这里插入图片描述

5.1__str__() 字符串方法

print(对象) 或者 str(对象) 时,会自动调用__str__()方法。默认情况下,该方法返回的是对象的内存地址。

class Student:
    name = None
    age = None
    def __init__(self, name, age):
        self.name = name
        self.age = age

stu = Student("tom", 15)
print(stu)
print(str(stu))

输出结果:

<__main__.Student object at 0x01C6CB68>
<__main__.Student object at 0x01C6CB68>

但我们基本不关心对象的内存地址。因此,可以通过__str__()方法控制直接输出对象把对象转换成字符串的行为。

class Student:
    name = None
    age = None
    def __init__(self, name, age):
        self.name = name
        self.age = age
    # 返回值:字符串;内容:自行定义
    def __str__(self):
        return f"Student对象:name={self.name}, age={self.age}"

stu = Student("tom", 15)
print(stu)
print(str(stu))

输出结果:

Student对象:name=tom, age=15
Student对象:name=tom, age=15

5.2__lt__() 小于比较方法

不能像下面这样直接比较两个对象:

class Student:
    name = None
    age = None
    def __init__(self, name, age):
        self.name = name
        self.age = age

stu1 = Student("tom", 15)
stu2 = Student("jerry", 9)
print(stu1 < stu2)
print(stu1 > stu2)

在这里插入图片描述
在类中实现__lt__()方法,可以同时完成小于和大于两种比较。(虽然名称是小于号比较方法,但是小于和大于都能比较)

class Student:
    name = None
    age = None
    def __init__(self, name, age):
        self.name = name
        self.age = age
    # 传入参数:other,即另一个对象
    # 返回值:True或False
    # 内容:自行定义
    def __lt__(self, other):
        return self.age < other.age

stu1 = Student("tom", 9)
stu2 = Student("jerry", 15)
print(stu1 < stu2)#True
print(stu1 > stu2)#False

5.3__le__() 小于等于比较方法

__le__()可以用于 <= 或 >= 两种比较运算符上。

class Student:
    name = None
    age = None
    def __init__(self, name, age):
        self.name = name
        self.age = age
    # 传入参数:other,即另一个对象
    # 返回值:True或False
    # 内容:自行定义
    def __le__(self, other):
        return self.age <= other.age

stu1 = Student("tom", 9)
stu2 = Student("jerry", 15)
stu3 = Student("smith", 15)
print(stu1 <= stu2)#True
print(stu1 >= stu2)#False
print(stu2 <= stu3)#True
print(stu2 >= stu3)#True

5.4__eq__() 比较运算符实现方法

不实现__eq__()方法,对象之间可以进行 “==” 比较,但比较的是内存地址。所以不同对象比较一定是 False。

class Student:
    name = None
    age = None
    def __init__(self, name, age):
        self.name = name
        self.age = age

stu1 = Student("tom", 9)
stu2 = Student("tom", 9)
stu3 = Student("tom", 15)
print(stu1 == stu2)#False
print(stu1 == stu3)#False

实现了__eq__()方法后,就可以按照需要来判断两个对象是否相等。

class Student:
    name = None
    age = None
    def __init__(self, name, age):
        self.name = name
        self.age = age
    # 传入参数:other,即另一个对象
    # 返回值:True或False
    # 内容:自行定义
    def __eq__(self, other):
        return self.age == other.age

stu1 = Student("tom", 9)
stu2 = Student("tom", 9)
stu3 = Student("tom", 15)
print(stu1 == stu2)#True
print(stu1 == stu3)#False

6. 封装

私有成员:包括私有成员变量和私有成员方法。

定义私有成员的方式
私有成员变量:变量名以__开头(2个下划线)
私有成员方法:方法名以__开头(2个下划线)

在这里插入图片描述

私有成员无法被类对象使用,但是可以被类中的其它成员使用。

在这里插入图片描述

练习:
设计一个手机类,内部包含:
(1) 私有成员变量:__is_5g_enable,类型bool,True表示开启 5g,False表示关闭。
(2) 私有成员方法:__check_5g(),用于判断私有成员__is_5g_enable 的值。
  若为 True,打印输出:5g开启。
  若为 False,打印输出:5g关闭,使用4g网络。
(3) 公开成员方法:call_by_5g(),它会:
  调用私有成员方法__check_5g(),判断 5g 网络状态。
  打印输出正在通话中。

输出样例:
5g关闭,使用4g网络
正在通话中

class Phone:
    # 手机的内部信息不需要用户知道,所以做成私有的
    __is_5g_enable = False

    def __check_5g(self):
        if self.__is_5g_enable == True:
            print("5g开启")
        else:
            print("5g关闭,使用4g网络")
    # 用户只需要能够打电话
    def call_by_5g(self):
        self.__check_5g()
        print("正在通话中")

phone = Phone()
phone.call_by_5g()

输出结果:

5g关闭,使用4g网络
正在通话中

7. 继承

7.1 继承的概念

继承:从父类继承(复制)非私有的成员变量和成员方法,分为单继承和多继承。

7.2 单继承

语法:

class 类名(父类名):
	类内容体

【例】

class Phone:
    IMEI = None         #序列号
    producer = "HM"     #生产厂商
    def call_by_4g(self):
        print("4g通话")

class NewPhone(Phone):
    face_id = "10010"   #面部识别id
    def call_by_5g(self):
        print("5g通话")

phone = NewPhone()
print(phone.producer)#HM
phone.call_by_4g()#4g通话
print(phone.face_id)#10010
phone.call_by_5g()#5g通话

7.3 多继承

Python 的类也支持多继承,即:一个类可以继承多个父类。
多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级。即:先继承的保留,后继承的被覆盖。

语法:

class 类名(父类1,父类2,...,父类N):
	类内容体

【例】

class Phone:
    producer = "ITCAST" #生产厂商
    def call_by_4g(self):
        print("4g通话")

class NFCReader:
    nfc_type = "第五代"
    producer = "HM"
    def read_card(self):
        print("NFC读卡")
    def write_card(self):
        print("NFC写卡")

class Myphone(Phone, NFCReader):
    # 类体中不想写内容了,但是又不能空着,可以用pass占位
    pass

my_phone = Myphone()
#ITCAST, Phone类中的producer属性
print(my_phone.producer)#ITCAST
my_phone.call_by_4g()#4g通话
print(my_phone.nfc_type)#第五代
my_phone.read_card()#NFC读卡
my_phone.write_card()#NFC写卡

7.3 pass 关键字

pass:占位语句,用来保证函数、方法或类定义的完整性,表示无内容,空的意思。具体用法见 7.4 代码中的 Myphone 类。

7.5 复写父类成员和调用父类成员

复写父类成员
子类继承父类的成员属性和成员方法后,若对其 “不满意”,可以进行复写。即:在子类中重新定义同名的属性或方法。

class Phone:
    producer = "ITCAST" #生产厂商
    def call_by_5g(self):
        print("父类中的5g通话")

class Myphone(Phone):
    producer = "ITHEIMA" #生产厂商
    def call_by_5g(self):
        print("子类中的5g通话")

my_phone = Myphone()
print(my_phone.producer)#ITHEIMA
my_phone.call_by_5g()#子类中的5g通话

调用父类同名成员
子类复写了父类成员后,子类对象调用成员时,就会调用复写后的新成员。若要使用父类成员,可以用以下两种调用方式:

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

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

class Phone:
    producer = "ITCAST" #生产厂商
    def call_by_5g(self):
        print("父类中的5g通话")

class Myphone(Phone):
    producer = "ITHEIMA" #生产厂商
    def call_by_5g(self):
    	# 有时可以通过这样,既添加新功能,又利用父类原有功能
        # 方式1
        print(f"父类中的producer={Phone.producer}")
        Phone.call_by_5g(self)
        # 方式2
        print(f"父类中的producer={super().producer}")
        super().call_by_5g()

my_phone = Myphone()
my_phone.call_by_5g()

输出结果:

父类中的producer=ITCAST
父类中的5g通话
父类中的producer=ITCAST
父类中的5g通话

注意:只可以在子类内部调用父类的同名成员,子类对象默认调用子类复写的成员。

8. 类型注解

类型注解:在代码中显式地说明数据的类型。

主要功能:
(1) 帮助第三方 IDE 工具(如PyCharm)对代码进行类型推断,协助代码提示。
(2) 帮助开发者对变量进行类型注释。

类型注解支持:变量的类型注解、函数(方法)形参列表和返回值的类型注解。

8.1 变量的类型注解

方式1:

# 基础数据类型注解
var_1: int = 10
var_2: float = 3.1415926
var_3: bool = True
var_4: str ="itheima"
# 对象类型注解
class Student:
    pass
stu: Student = Student()
# 容器简单类型注解
my_list1: list = [1, 2, 3]
my_tuple1: tuple = (1, 2, 3)
my_set1: set = {1, 2, 3}
my_dict1: dict = {"itheima":666}
my_str1: str = "itheima"
# 容器详细类型注解
my_list2: list[int] = [1, 2, 3]
my_tuple2: tuple[str, int, bool] = ("itheima", 666, True)
my_set2: set[int] = {1, 2, 3}
my_dict2: dict[str, int] = {"itheima": 666}

注意事项:
元组设置详细类型注解,需要将每一个元素都标记出来。
字典设置详细类型注解,需要两个类型,第一个是 key,第二个是 value。

方式2:

除了上面的方式,也可以在注释中进行变量的类型注解。
语法:#type:类型

在这里插入图片描述
为变量设置注解时,显式的变量定义一般无需注解。因为就算不写注解,也能够明确知晓变量的类型。

在这里插入图片描述
当无法直接看出变量类型时,会添加变量的类型注解。

在这里插入图片描述

注意:类型注解仅仅是建议性的,不是强制性的。所以,若数据类型与注解类型无法对应,也不会导致错误。如下面代码不会报错:

var_1: int = "itheima"
var_2: str = 123

8.2 函数和方法的类型注解

8.2.1 函数 / 方法形参的类型注解

如下图所示,编写了函数 / 方法,使用形参 data 的时候,工具没有任何提示。
在调用函数 / 方法传入参数的时候,工具无法提示参数类型。
这都是因为,在定义函数 / 方法的时候,没有对形参进行注解。
在这里插入图片描述
函数 / 方法的形参类型注解语法:

def 函数/方法名(形参名: 类型, 形参名: 类型,....):
	pass

注解后的效果:

在这里插入图片描述

在这里插入图片描述

8.2.2 函数 / 方法返回值的类型注解

语法(使用->符号):

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

【例】

def func(data: list) -> list:
    return data

另外,注解仅仅是建议性的,不是强制性的。所以,若数据类型与注解类型无法对应,也不会导致错误。

在这里插入图片描述

8.3 Union 联合类型注解

8.3.1 为什么需要 Union 联合类型注解

如下面代码,当列表元素中只有一种数据类型、字典的 value 只有一种数据类型时,可以利用前面的方法方便地进行详细的类型注解。

my_list: list[int] = [1, 2, 3]
my_dict: dict[str, int] ={"age": 11, "num": 3}

但是,若列表元素中有多于一种数据类型、字典的 value 多余一种数据类型时,利用前面的方法进行详细的类型注解就行不通了。

my_list = [1, 2, "itcast", "itheima"]
my_dict= {"name": "周杰轮", "age": 31}

这时,可以使用 Union 定义联合类型注解:Union[类型,...,类型]

8.3.2 Union 联合类型注解的使用

变量的联合类型注解:

from typing import Union
# Union[str, int]表示类型是str和int中的一种
my_list: list[Union[str, int]] = [1, 2, "itcast", "itheima"]
my_dict: dict[str, Union[str, int]]= {"name": "周杰轮", "age": 31}

函数的联合类型注解:

在这里插入图片描述

9. 多态

9.1 多态的概念

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

多态常常作用在继承关系上,比如:函数 / 方法的形参声明接收父类对象,但实际传入子类对象进行工作。

class Animal:
    def speak(self):
        pass

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

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

def make_noise(animal: Animal):
    animal.speak()

cat = Cat()
dog = Dog()
make_noise(cat)#喵喵喵
make_noise(dog)#汪汪汪

9.2 抽象类(接口)的编程思想

在 9.1 的代码中,父类 Animal 的 speak 方法内容为空。以使父类确定有哪些方法,子类决定方法的具体实现。

抽象方法:方法体为空(只有pass)的方法称之为抽象方法,如:父类 Animal 的 speak 方法。
抽象类:含有抽象方法的类称之为抽象类,如:Animal 类。

为什么要使用抽象类
举个例子:空调有一定的国家标准,假设为:可以制冷、可以制热、左右摆风……有了这个标准,各个厂家可以自行地去具体实现。
在这里插入图片描述
抽象类就好比定义一个标准,它包含一些抽象方法,要求子类必须实现,从而达到约束子类的目的。
定义了抽象类之后,并不会直接使用,即:不会直接用抽象类创建对象。真正工作的是抽象类的子类。

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

make_cool(Midea_AC())#美的冷风
make_cool(GREE_AC())#格力冷风

10. 综合案例

所需文件:
链接:https://pan.baidu.com/s/1nI0lFNOxptAmQ3kh5UHMEQ?pwd=sumg
提取码:sumg

要求:读取 “2011年1月销售数据.txt” 和 “2011年2月销售数据JSON.txt” 两个文件,统计每天各省的销售额之和,并用柱状图展示。两个文件的内容形式分别为:

2011年1月销售数据.txt:
在这里插入图片描述
2011年2月销售数据JSON.txt:

在这里插入图片描述

效果图:
在这里插入图片描述

代码(共三个文件):

data_define.py

class Record:
    def __init__(self, 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}"

file_define.py

import json
from data_define import Record

class FileRead:     #文件读取抽象类
    def read_data(self) -> list[Record]:
        pass

class TextFileRead(FileRead):   #文本文件读取
    def __init__(self, path):   #传入文件路径
        self.path = path

    def read_data(self) -> list[Record]:
        f = open(self.path, "r", encoding="utf-8")
        records_list: list[Record] = [] #保存文件每行数据对象的列表
        for line in f.readlines():  #f.readlines()返回列表,列表中每个元素是一行数据
            line = line.strip()     #去掉”\n“
            data_list = line.split(",")#每行数据按照”,“拆分,存储在列表中
            # 每行数据的列表生成一个对象,追加到records_list中
            records_list.append(Record(data_list[0], data_list[1], int(data_list[2]), data_list[3]))
        f.close()
        return records_list

class JsonFileRead(FileRead):      #json文件读取
    def __init__(self, path):      #传入文件路径
        self.path = path

    def read_data(self) -> list[Record]:
        f = open(self.path, "r", encoding="utf-8")
        records_list: list[Record] = []#保存文件每行数据对象的列表
        for line in f.readlines():      #f.readlines()返回列表,列表中每个元素是一行数据
            data_dict = json.loads(line)#不用去掉”\n“,直接把json字符串转换成字典,为什么?
            # 每行数据的字典生成一个对象,追加到records_list中
            records_list.append(Record(data_dict["date"], data_dict["order_id"], int(data_dict["money"]), data_dict["province"]))
        f.close()
        return records_list

main.py

from file_define import FileRead, TextFileRead, JsonFileRead
from data_define import Record
from pyecharts.charts import Bar
from pyecharts.options import *
from pyecharts.globals import ThemeType

#text_file_read = TextFileRead("file/数据分析案例/2011年1月销售数据.txt")
text_file_read = TextFileRead("E:/pythonProject/testpro01/file/数据分析案例/2011年1月销售数据.txt")
jan_data_list = text_file_read.read_data()
json_file_read = JsonFileRead("E:/pythonProject/testpro01/file/数据分析案例/2011年2月销售数据JSON.txt")
feb_data_list = json_file_read.read_data()
# 合并两个列表
all_data_list = jan_data_list + feb_data_list
date_money_dict = {}

for record in all_data_list:
    if record.date in date_money_dict:
        date_money_dict[record.date] += record.money
    else:
        date_money_dict[record.date] = record.money

# bar = Bar(init_opts=InitOpts(theme=ThemeType.LIGHT))#也可以
bar = Bar({"theme":ThemeType.LIGHT})
# date_money_dict.keys()、date_money_dict.values()得到的都是dict_keys类型
bar.add_xaxis(list(date_money_dict.keys()))
# label_opts=LabelOpts(is_show=False):每个柱不显示数字
bar.add_yaxis("销售额",list(date_money_dict.values()), label_opts=LabelOpts(is_show=False))
bar.set_global_opts(
    # 柱状图标题
    title_opts=TitleOpts(title="每日销售额柱状图", pos_left="center", pos_bottom="1%")
)
bar.render("每日销售额.html")
  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值