10-python面向对象
class Student: # 定义Student类,有两个属性name和agename=Noneage=Nonestu1 = Student() # 创建对象stu1,包含student中的两个属性name和age
stu2 = Student() # 创建对象stu2stu1.name="jack" # 赋值
stu1.age=18
stu2.name="peter"
stu2.age=17print(stu1.age) # 18
print(stu2.name) # peter
文章目录
1.成员变量和成员方法
(1)成员变量:在类中定义的变量/属性叫成员变量
(2)成员方法:在类中定义的函数/行为叫成员方法,类外定义的函数仍叫做函数
在成员方法中必须使用self,在成员方法中访问成员变量也必须使用self;对于只有self的成员方法,调用时可按照无参处理;对于有多个参数的成员方法,调用时可忽略self传参
class Student():name=Noneage=Nonedef f1(self): # 成员方法print(self.name) # 在成员方法中访问成员变量必须使用selfdef 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=Noneage=Nonedef __init__(self): # 构造方法print(1)
stu=Student() # 创建对象
# 输出:1
创建对象时,可将参数传递给构造方法。例如在创建对象时完成赋值
class Student():name=Noneage=Nonedef __init__(self,name,age): # 构造方法self.name=name # 传入的name给stu的name赋值self.age=agestu=Student("jack",18) # 创建对象stu时赋值
print(stu.name) # 输出:jack
以上代码可进一步简化,在构造方法中对name和age创建并赋值
class Student():# name=None# age=Nonedef __init__(self,name,age):self.name=nameself.age=agestu=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=nameself.age=agedef __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=nameself.age=agedef __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为Trueprint(a1<a3) # False
# 正常比较:a1的age<a3的age为False
若lf方法内改为大于号,则比较结果相反
class A:def __init__(self,name,age):self.name=nameself.age=agedef __lt__(self,x): return self.age>x.age # 改为大于号a1=A("jack",18)
a2=A("peter",17)print(a1>a2) # Flase
# a1的age>a2的age,但输出Falseprint(a1>=a2) # TypeError: '>=' not supported,可使用下文的le比较
(3)gt
对应大于号,若gt方法内仍为大于号则正常比较,否则结果相反
class A:def __init__(self,name,age):self.name=nameself.age=agedef __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=nameself.age=agedef __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=nameself.age=agedef __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 = nameself.age = agedef __eq__(x,y):return x.age == y.agea1 = 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 = nameself.age = age# def __eq__(x,y):# return x.age == y.agea1 = 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=18def f(self):print(self.__age) # 类内可以访问私有变量
a=A()
a.f() # 18
[练习]
[解]
class Phone:__is_5g_enable=Nonedef __check_5g(self,x):self.__is_5g_enable=xif 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=Truedef __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 = Noneage = None__weight = None # 子类无法直接访问私有属性def __init__(self,n,a,w): # 构造方法self.name=nself.age=aself.__weight=w def speak(self):print("我叫%s,今年%d岁" % (self.name,self.age))class Student(People): # Student继承Peoplegrade = Nonedef __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) # errors=Student("jack",18,200,100)
s.speak()
"""
我叫jack,今年18岁,考了100分
AttributeError: 'Student' object has no attribute '_Student__weight'
"""
(2)多继承
class People:name = Noneage = None__weight = None # 子类无法直接访问私有属性def __init__(self,n,a,w):self.name=nself.age=aself.__weight=wdef speak(self):print("我叫%s,今年%d岁" % (self.name,self.age))class Student(People): # Student继承Peoplegrade = Nonedef __init__(self,n,a,w,g):People.__init__(self,n,a,w) # 调用父类的构造方法self.grade=gdef speak(self): # 重写父类的方法print("我叫%s,今年%d岁,考了%d分"%(self.name,self.age,self.grade))class Speaker:topic=Nonename=Nonedef __init__(self,n,t):self.name=nself.topic=tdef speak(self):print("我叫%s,我演讲的主题是%s"%(self.name,self.topic))class Sample(Speaker,Student): # Sample类继承Speaker和Studentdef __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赋值jackprint(self.name)self.name = "peter"print(self.name)A.f(self) # 方式二:使用此方法调用必须加selfprint(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=dateself.ID=IDself.price=priceself.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(",") # 数据分割存入adata=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,File2file1=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 # 在字典中根据键查找对应的值,累加moneyelse: # 当前数据不在字典中,创建新键值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,File2file1=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+ffile2data_dict={} # 用字典存储数据
for x in all_data:if x.date in data_dict.keys(): # 如果当前数据的日期在data_dict字典中,进行累加data_dict[x.date]+=x.price # 在字典中根据键查找对应的值,累加moneyelse: # 当前数据不在字典中,创建新键值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=dateself.ID=IDself.price=priceself.province=province
file_define.py
import jsonfrom data_define import Data
class FileReader:def read_data(self):passclass 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(",") # 数据分割存入adata=Data(a_list[0], a_list[1], int(a_list[2]), a_list[3]) # 调用Data函数赋值Data_list.append(data) # 存入列表f.close()return Data_listclass 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
效果图