10-python面向对象

class Student:  # 定义Student类,有两个属性name和age
    name=None
    age=None

stu1 = Student()  # 创建对象stu1,包含student中的两个属性name和age
stu2 = Student()  # 创建对象stu2

stu1.name="jack"  # 赋值
stu1.age=18
stu2.name="peter"
stu2.age=17

print(stu1.age)  # 18
print(stu2.name)  # peter

1.成员变量和成员方法

(1)成员变量:在类中定义的变量/属性叫成员变量
(2)成员方法:在类中定义的函数/行为叫成员方法,类外定义的函数仍叫做函数

在成员方法中必须使用self,在成员方法中访问成员变量也必须使用self;对于只有self的成员方法,调用时可按照无参处理;对于有多个参数的成员方法,调用时可忽略self传参

class Student():
    name=None
    age=None
    def f1(self):  # 成员方法
        print(self.name)  # 在成员方法中访问成员变量必须使用self
    def f2(self,x):  # 成员方法
        print(self.name)
        print(x)

stu=Student()  # 定义对象stu
stu.name="jack"
stu.f1()  # 调用f1
stu.f2("hello")  # 调用f2

"""输出
jack
jack
hello
"""

2.构造方法

用 _ _ init_ _(self) 表示构造方法,在创建对象时,构造方法会自动执行

class Student():
    name=None
    age=None
    def __init__(self):  # 构造方法
        print(1)
stu=Student()  # 创建对象
# 输出:1

创建对象时,可将参数传递给构造方法。例如在创建对象时完成赋值

class Student():
    name=None
    age=None
    def __init__(self,name,age):  # 构造方法
        self.name=name  # 传入的name给stu的name赋值
        self.age=age

stu=Student("jack",18)  # 创建对象stu时赋值
print(stu.name)  # 输出:jack

以上代码可进一步简化,在构造方法中对name和age创建并赋值

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

stu=Student("jack",18)
print(stu.name)

3.常见内置方法

init、str、lt、gt、le、eq

(1)str

通过使用 _ _ str _ _可返回特定的字符串

class A:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):
        return f"hello,{
      
      self.age}岁的{
      
      self.name}"  # 返回特定的字符串
    
a=A("jack",18)
print(a)  # hello,18岁的jack

(2)lt

lt对应的是小于号“<”,可通过lt方法指明比较的对象

若lf方法内仍为小于号,则正常比较

class A:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __lt__(self,x):  # 明确比较的是age;此处的self和x并非关键字,可任意替换
        return self.age<x.age # 小于号,正常比较
    
a1=A("jack",18)
a2=A("peter",17)
a3=A("nihao",16)

print(a1>a2)  # True
# a1当做self传入,a2当做x传入
# 正常比较:a1的age>a2的age为True

print(a1<a3)  # False
# 正常比较:a1的age<a3的age为False

若lf方法内改为大于号,则比较结果相反

class A:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __lt__(self,x): 
        return self.age>x.age # 改为大于号
    
a1=A("jack",18)
a2=A("peter",17)

print(a1>a2)  # Flase
# a1的age>a2的age,但输出False

print(a1>=a2)  # TypeError: '>=' not supported,可使用下文的le比较

(3)gt

对应大于号,若gt方法内仍为大于号则正常比较,否则结果相反

class A:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __gt__(y,x):
        return y.age>x.age # gt大于号正常比较

a1=A("jack",18)
a2=A("peter",17)

print(a1>a2)  # True

(4)le

对应小于等于

class A:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __le__(self,x):
        return self.age<=x.age  # 小于等于,正常比较

a1=A("jack",18)
a2=A("peter",17)
a3=A("nihao",18)

print(a1>=a2)  # True
print(a1>=a3)  # True
print(a1<a2)  # TypeError:le只能比较≤或≥

若le方法中改为≥,则结果相反(相等的不反)

class A:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __le__(self,x):
        return self.age>=x.age  # 大于等于号,结果相反

a1=A("jack",18)
a2=A("peter",17)
a3=A("nihao",18)

print(a1>=a2)  # False
print(a1>=a3)  # True

(5)eq

判断对象的指定属性是否相等

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

    def __eq__(x,y):
        return x.age == y.age

a1 = A("jack", 18)
a2 = A("peter", 17)
a3 = A("aaa", 18)

print(a1 == a2)  # Flase
print(a1 == a3)  # True

如果直接比较而不使用eq方法,则比较的是两个对象的内存地址,一定不同,返回False

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

    # def __eq__(x,y):
    #     return x.age == y.age

a1 = A("jack", 18)
a2 = A("peter", 18)
print(a1 == a2)  # Flase

4.私有成员

私有属性在类外部无法直接进行访问
_ _ 变量 # 私有变量
_ _ 方法 # 私有方法

class A:
    name="jack"
    __age=18 # 私有变量

    def f1(self):
        print(1)
    def __f2(self):  # 私有方法
        print(2)
a=A()
print(a.name)  # jack
print(a.__age) # AttributeError: 'A' object has no attribute '__age'
a.f1()  # 1
a.__f2 # AttributeError: 'A' object has no attribute '__f2'

类内访问

class A:
    __age=18
    def f(self):
        print(self.__age) # 类内可以访问私有变量
a=A()
a.f() # 18

[练习]

在这里插入图片描述
[解]

class Phone:
    __is_5g_enable=None
    def __check_5g(self,x):
        self.__is_5g_enable=x
        if self.__is_5g_enable:
            print("5g开启")
        else:
            print("5g关闭,使用4g网络")
    def call_by_5g(self,x):
        self.__check_5g(x)
        print("正在通话中")
phone=Phone()
phone.call_by_5g(True)
phone.call_by_5g(False)
"""
5g开启
正在通话中
5g关闭,使用4g网络
正在通话中
"""
class Phone:
    __is_5g_enable=True
    def __check_5g(self):
        if self.__is_5g_enable:
            print("5g开启")
        else:
            print("5g关闭,使用4g网络")
    def call_by_5g(self):
        self.__check_5g()
        print("正在通话中")
phone=Phone()
phone.call_by_5g()
"""
5g开启
正在通话中
"""

5.继承

(1)单继承

class People:
    name = None
    age = None
    __weight = None # 子类无法直接访问私有属性
    def __init__(self,n,a,w):  # 构造方法
        self.name=n
        self.age=a
        self.__weight=w   
    def speak(self):
        print("我叫%s,今年%d岁" % (self.name,self.age))
        
class Student(People):  # Student继承People
    grade = None
    def __init__(self,n,a,w,g):
        People.__init__(self,n,a,w) # 调用父类的构造方法
        self.grade=g  # 添加新功能
    def speak(self): # 重写父类的方法
        print("我叫%s,今年%d岁,考了%d分"%(self.name,self.age,self.grade))
        print(self.__weight) # error
        
s=Student("jack",18,200,100)
s.speak()
"""
我叫jack,今年18岁,考了100分
AttributeError: 'Student' object has no attribute '_Student__weight'
"""

(2)多继承

class People:
    name = None
    age = None
    __weight = None # 子类无法直接访问私有属性
    def __init__(self,n,a,w):
        self.name=n
        self.age=a
        self.__weight=w
    def speak(self):
        print("我叫%s,今年%d岁" % (self.name,self.age))

class Student(People):  # Student继承People
    grade = None
    def __init__(self,n,a,w,g):
        People.__init__(self,n,a,w) # 调用父类的构造方法
        self.grade=g
    def speak(self): # 重写父类的方法
        print("我叫%s,今年%d岁,考了%d分"%(self.name,self.age,self.grade))

class Speaker:
    topic=None
    name=None
    def __init__(self,n,t):
        self.name=n
        self.topic=t
    def speak(self):
        print("我叫%s,我演讲的主题是%s"%(self.name,self.topic))

class Sample(Speaker,Student):  # Sample类继承Speaker和Student
    def __init__(self,n,a,w,g,t):
        Student.__init__(self,n,a,w,g)
        Speaker.__init__(self,n,t)
        
sample=Sample("jack",18,200,100,"nihao")
sample.speak() # Student和Speaker都有speak方法,会调用先继承的方法(排在前面的)Speaker
"""输出
我叫jack,我演讲的主题是nihao
"""

6.重写

(1)变量

①在类外调用父类

class A:
    name="jack"
class B(A):
    name="peter"  # 子类重写父类的变量

b=B()
print(b.name)  # peter

# 调用父类方式1:使用super(子类,子类对象).变量 的方式可以访问父类的属性
print(super(B,b).name)  # jack

#调用父类方式2
print(A.name) # jack

②在子类调用父类

class A:
    name="jack"
class B(A):
    def f(self):
        name="peter"  # 子类重写父类的属性
        print(name)
        print(super().name)  # 方式一:在子类中使用super可以省略参数
        print(super(B,b).name)  # 方式二:不省略参数
        print(A.name)  # 方式三
b=B()
b.f()
"""
peter
jack
jack
jack
"""

(2)方法

①在类外调用父类

class A:
    def f(self):
        return 1
class B(A):
    def f(self):
        return 2
b=B()
print(b.f()) # 2
print(super(B,b).f()) # 1 调用了父类的方法

②在子类调用父类

class A:
    def f(self):
        self.name="jack"
class B(A):
    def f(self):
        self.name = "peter"
        print(self.name)
        super().f() # 方式一:调用了父类的方法,给name赋值jack
        print(self.name)

        self.name = "peter"
        print(self.name)
        A.f(self) # 方式二:使用此方法调用必须加self
        print(self.name)
b=B()
b.f()
"""输出
peter
jack
peter
jack
"""

7.类型注解

(1)变量的类型注解

用途:帮助IDE对代码类型判断、便于查看(备注)

在冒号后面指明类型

x:int =10 # x是int型的变量,值为10
y:float=2.5
x1:list=[1,2,3] # x1是list型
x2:list[int] =[1,2,3] # x2是list型,里面的元素是int型
x3:tuple[str,int,bool]=("hello",18,True)
x4:dict[str,int]={
    
    "hello",18}

注释注解

在这里插入图片描述

(2)函数(方法)的类型注解

在这里插入图片描述

注明a和b的类型,便于IDE识别

在这里插入图片描述

通过"->"指明函数的返回值类型

def add(a,b) -> int:
    return a+b

(3)Union类型

from typing import Union
a=[1,2,"jack",3] # 普通定义列表

b:list[Union[str,int]]=[14,23,"jackw",4]
# 指明b是一个列表,由str和int类型的数据联合组成

c=dict[str,Union[str,int]]={
    
    "name":"jack","age":18}
# 字典,键str,值str和int组成

对于函数同样可以使用Union

在这里插入图片描述

8.多态与抽象

多态指对于某个行为,不同对象有不同反应,很好地解决了函数的重名问题

class Animal: # 抽象类
    def speak(self): # 抽象方法
        pass
class Dog(Animal):
    def speak(self):
        print("汪汪汪")
class Cat(Animal):
    def speak(self):
        print("喵喵喵")
def shout(x):
    x.speak()

dog=Dog()
cat=Cat()
shout(cat) # 喵喵喵
shout(dog) # 汪汪汪
# 同样的shout函数,传入的对象不同,会得到不同的结果

在此形式中,父类只需要定义方法,而子类负责实现。我们把这种方法体是pass的方法叫做抽象方法,把含有抽象方法的类叫做抽象类。可以看出,抽象类不能独立工作,也不会去构建对象,它必须被子类继承并重写后才有意义。

[综合练习]

数据获取下载

在这里插入图片描述
有2份数据文件,计算每日的销售额并以柱状图表的形式进行展示(如下图),要求使用面向对象的思想

参考思想如下:
①设计一个类,完成对数据的封装
②设计一个抽象类,定义文件读取的相关功能,并用子类实现具体功能
③读取文件,生成数据对象
④计算每一天的销售额
⑤图像绘制

在这里插入图片描述
文件1:str格式(订单日期、订单ID、销售额、销售省份)

在这里插入图片描述

文件2:JSON格式

在这里插入图片描述

[解]
1.新建包和main文件
在这里插入图片描述
创建data_define.py文件,在类中对数据进行封装

class Data:
    def __init__(self,date,ID,price,province):
        self.date=date
        self.ID=ID
        self.price=price
        self.province=province

2.定义抽象类和子类
创建文件相关定义file_define.py

定义抽象类

class FileReader:
    def read_data(self): # 抽象方法
        pass

针对文件1:字符串文件的处理,继承父类,重写方法

class File1(FileReader):  # 针对文件1:字符串类文件
    def __init__(self, path):
        self.path = path  # path存放文件路径

    def read_data(self):
        f = open(self.path, "r", encoding="UTF-8")
        Data_list = []  # 用于存放数据
        for x in f.readlines():
            x = x.strip()  # 处理每一行数据后面的换行符
            a_list = x.split(",")  # 数据分割存入a
            data=Data(a_list[0], a_list[1], int(a_list[2]), a_list[3])  # 调用Data函数赋值
            Data_list.append(data)  # 存入列表
        f.close()
        return Data_list

针对文件2:JSON文件的处理,继承父类,重写方法

class File2(FileReader):
    def __init__(self, path):
        self.path = path  # path存放文件路径

    def read_data(self):
        f = open(self.path, "r", encoding="UTF-8")
        Data_list = []  # 用于存放数据
        for x in f.readlines():
            a_dict=json.loads(x)
            data=Data(a_dict["date"],a_dict["order_id"],int(a_dict["money"]),a_dict["province"]) # 通过键获取值,进行赋值
            Data_list.append(data)
        f.close()
        return Data_list

3.读取文件,生成数据对象
在main.py中

from data_define import Data
from file_define import FileReader,File1,File2

file1=File1("D:/面向对象资料1/2011年1月销售数据.txt")
file2=File2("D:/面向对象资料1/2011年2月销售数据JSON.txt")

ffile1=file1.read_data() # 调用方法,进行处理
ffile2=file2.read_data() # 调用方法,进行处理
all_data=ffile1+ffile2 # 全部数据整合到了列表中

4.计算每一天的销售额
在main.py中

data_dict={
    
    } # 用字典存储数据(字典的键是不能重复的)
for x in all_data:
    if x.date in data_dict.keys(): # 如果当前数据的日期在data_dict字典中,进行累加
        data_dict[x.date]+=x.price # 在字典中根据键查找对应的值,累加money
    else: # 当前数据不在字典中,创建新键值
        data_dict[x.date]=x.price

输出:{‘2011-01-01’: 59242, ‘2011-01-02’: 58479, ‘2011-01-03’: 52336, ‘2011-01-04’: 35696, ‘2011-01-05’: 58860…

5.绘图

from pyecharts.charts import Bar
from pyecharts.globals import ThemeType
from pyecharts.options import TitleOpts, LabelOpts, InitOpts
bar=Bar(init_opts=InitOpts(theme=ThemeType.LIGHT)) # 颜色
bar.add_xaxis(list(data_dict.keys())) # x轴为日期,需要转成列表
bar.add_yaxis("销售额",list(data_dict.values()),label_opts=LabelOpts(is_show=False)) # "销售额"为图例,y轴为销售额,不显示每个柱状图的数据
bar.set_global_opts(
    title_opts=TitleOpts(title="每日销售额") # 标题
)
bar.render("每日销售额柱状图.html")

6.完整代码
main.py

from pyecharts.charts import Bar
from pyecharts.globals import ThemeType
from pyecharts.options import TitleOpts, LabelOpts, InitOpts
from file_define import File1,File2

file1=File1("D:/面向对象资料1/2011年1月销售数据.txt")
file2=File2("D:/面向对象资料1/2011年2月销售数据JSON.txt")

ffile1=file1.read_data() # 调用方法,进行处理
ffile2=file2.read_data() # 调用方法,进行处理
all_data=ffile1+ffile2

data_dict={
    
    } # 用字典存储数据
for x in all_data:
    if x.date in data_dict.keys(): # 如果当前数据的日期在data_dict字典中,进行累加
        data_dict[x.date]+=x.price # 在字典中根据键查找对应的值,累加money
    else: # 当前数据不在字典中,创建新键值
        data_dict[x.date]=x.price

# 绘图
bar=Bar(init_opts=InitOpts(theme=ThemeType.LIGHT)) # 颜色
bar.add_xaxis(list(data_dict.keys())) # x轴为日期,需要转成列表
bar.add_yaxis("销售额",list(data_dict.values()),label_opts=LabelOpts(is_show=False)) # "销售额"为图例,y轴为销售额,不显示每个柱状图的数据
bar.set_global_opts(
    title_opts=TitleOpts(title="每日销售额") # 标题
)
bar.render("每日销售额柱状图.html")

data_define.py

class Data:
    def __init__(self,date,ID,price,province):
        self.date=date
        self.ID=ID
        self.price=price
        self.province=province

file_define.py

import json

from data_define import Data
class FileReader:
    def read_data(self):
        pass

class File1(FileReader):  # 针对文件1:字符串类文件
    def __init__(self, path):
        self.path = path  # path存放文件路径

    def read_data(self):
        f = open(self.path, "r", encoding="UTF-8")
        Data_list = []  # 用于存放数据
        for x in f.readlines():
            x = x.strip()  # 处理每一行数据后面的换行符
            a_list = x.split(",")  # 数据分割存入a
            data=Data(a_list[0], a_list[1], int(a_list[2]), a_list[3])  # 调用Data函数赋值
            Data_list.append(data)  # 存入列表
        f.close()
        return Data_list

class File2(FileReader):
    def __init__(self, path):
        self.path = path  # path存放文件路径

    def read_data(self):
        f = open(self.path, "r", encoding="UTF-8")
        Data_list = []  # 用于存放数据
        for x in f.readlines():
            a_dict=json.loads(x)
            data=Data(a_dict["date"],a_dict["order_id"],int(a_dict["money"]),a_dict["province"]) # 通过键获取值,进行赋值
            Data_list.append(data)
        f.close()
        return Data_list

效果图

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45825865/article/details/130038906