Python中的面向对象
文章目录
类
- 实例化 : 根据类来创建对象
创建类
- 关键字 :
class
用于定义一个类 - 类名第一个字母通常要大写
__init__(self)
: 为构造函数, 第一个参数必须为self, 后面的参数是实例化类时传入的参数- 定义类的时候若无参数传入可以不用定义构造函数
class Car():
def __init__(self,name,color,brand):
# 定义对象属性(也叫变量)
self.name = name # 获取存储在形参name中的值,并将其存储到变量name 中,然后该变量被关联到当前创建的实例
self.color = color
self.brand = brand
def kind(self):
#类方法中的定义的对象属性是可以在同一个类中的方法中使用, 但是必须得在类方法中已经初始化才能给另一个类方法中
self.level = "很牛逼的车"
size = "large" # 这个才是类方法的变量
print(self.name+"的车类型为::"+self.type)
def drive(self):
print(self.name+"开着一辆"+self.color+self.type)
def evaluate(self):
print(self.level)
self
是一个指向实例本身的引用,让实例能够访问类中的属性和函数(方法)。- 每个与类相关联的函数(方法)调用都自动传递实参self
- 定义类函数(方法)时,必须要用
self
作为形参, 这也是普通方法与类方法的主要区别 - 类方法只需要传入一个
self
作为实参, 它就可以任意使用类中的属性和调用类中的方法
使用类
- 用类来创建对象 :
对象名 = 类名()
- 访问对象属性 :
对象名.属性
- 使用对象方法 :
对象名.方法()
# 使用Car类创建一个实例
car1 = Car("张三","粉色","奔驰") # 创建一个对象
# 可以指定传入的参数, 若没有指定, 则必须要按顺序传参
car2 = Car(name = "李四",brand="宝马",color="黑色") # 这种传参无需按顺序
print(car1.color) # 使用对象的属性
car1.kind() # 调用对象的方法
car1.drive() # 调用对象的方法
- 修改属性
- 可直接通过
对象.属性
进行赋值修改 - 也可通过定义类方法来将属性值传入方法中
- 通过调用方法来改变属性值
- 可直接通过
class Car():
def __init__(self,name,color,brand):
self.name = name
self.color = color
self.brand = brand
self.capacity = 5 # 座位数量, 给默认初始值
self.num = 0
self.introduce = "车的简介"
def update_capacity(self,capacity):
self.capacity = capacity
def update_num(self):
self.num += 1
def getNum(self):
print("num:%d"%self.num)
def getCapacity(self):
print("capacity:%d"%self.capacity)
def getIntroduce(self):
print("introduce:%s"%self.introduce)
def printGet(self):
self.getNum()
self.getCapacity()
self.getIntroduce()
xiaoming_car = Car("小明","绿色","本田")
xiaoming_car.printGet()
print("修改后的属性值")
# 第一种: 直接修改属性值
xiaoming_car.introduce = "这是个牛逼的车"
# 第二种: 通过类方法进行传参
xiaoming_car.update_capacity(3)
# 第三种: 通过类方法进行修改
xiaoming_car.update_num()
xiaoming_car.printGet()
输出 :
num:0
capacity:5
introduce:车的简介
修改后的属性值
num:1
capacity:3
introduce:这是个牛逼的车
类的专有方法
专有方法 | 说明 |
---|---|
__init__ | 构造函数,在生成对象时调用 |
__del__ | 析构函数,释放对象时使用 |
__repr__ | 打印,转换 |
__setitem__ | 按照索引赋值 |
__getitem__ | 按照索引获取值 |
__len__ | 获得长度 |
__cmp__ | 比较运算 |
__call__ | 函数调用 |
__add__ | 加运算 |
__sub__ | 减运算 |
__mul__ | 乘运算 |
__truediv__ | 除运算 |
__mod__ | 求余运算 |
__pow__ | 乘方 |
继承
class 类名(继承类)
, 即继承的父类写在定义类的后面的括号里- python是可以多继承的, 若有多个父类则用
,
隔开,
子类的构造方法
-
创建子类的实例时, Python 首先需要完成的任务是给父类的所有属性赋值。为此,子类的方法
__init__()
需要父类施以援手 -
super()
是特殊函数(方法),将父类和子类关联起来。父类也叫超类(superclass), 因此用super()
来将父类与子类的构造方法联系起来super().__init__()
, 该写法必须放在子类的构造函数里即__init__()
函数里
-
举个栗子
class Game():
# 默认参数要跟在非默认参数后面, 不然会报错
#比如: name 和 price = 0.00, name必须放前面, price带默认参数须放后面
def __init__(self,name,type="手游",price=0.00):
self.name = name
self.type = type
self.price = price
class Shop():
def action(self):
# print("欢迎来到{}游戏商店!".format(self.name)) # self.name使用子类的属性, 但是因为父类没有声明该属性, 若直接实例化父类, 再调用这个方法会报错
print("欢迎来到游戏商店!")
class Shooter(Game,Shop): # 继承多个父类
def __init__(self,name,size,type="手游",price=0.00):
# 使用super()即把父类下的构造方法内的属性都放到子类中, 使得子类得以继承父类的属性
super().__init__(name,type,price)
# 相当于下面的写法
# self.name = name
# self.type = type
# self.price = price
self.size = size #添加的子类属性, 仅在子类中生效
重写父类的方法
- 对于父类的方法,如果它的方法对子类不适用,都可在子类中对其进行重写
- 重写就是用父类的方法名重新定义一遍, 当子类与父类的方法名相同时, 解释器会优先调用子类方法的内容,从而忽略父类方法的内容
class Game():
# 默认参数要跟在非默认参数后面, 不然会报错
#比如: name 和 price = 0.00, name必须放前面, price带默认参数须放后面
def __init__(self,name,type="手游",price=0.00):
self.name = name
self.type = type
self.price = price
class Shop():
def action(self):
print("欢迎来到游戏商店!")
class Shooter(Game,Shop): # 继承多个父类
def __init__(self,name,size,type="手游",price=0.00):
# 使用super()即把父类下的构造方法内的属性都放到子类中, 使得子类得以继承父类的属性
super().__init__(name,type,price)
self.size = size
def action(self): # 重写父类Shop的方法
print("欢迎来到{}游戏商店!".format(self.name))
将实例用作属性
- 在类内实现另一个类的实例化, 即将别的类的实例作为该类的一个属性
- 这样的好处是可以将大型类拆分成多个协同工作的小类, 可以将一个庞大的类变小(指在代码行数上), 方便后续的开发和整理
class Game():
# 默认参数要跟在非默认参数后面, 不然会报错
#比如: name 和 price = 0.00, name必须放前面, price带默认参数须放后面
def __init__(self,name,type="手游",price=0.00):
self.name = name
self.type = type
self.price = price
def getName(self):
print("这是一款%s游戏"%self.name)
def getCost(self):
print("游戏的花费%d"%self.price)
def setPrice(self,set_price):
self.price = set_price
def promotePrice(self,cost):
self.price += cost
def getType(self):
print("这款游戏的类型是%s"%self.type)
class Shop():
def action(self):
# print("欢迎来到{}游戏商店!".format(self.name)) # self.name使用子类的属性, 但是因为父类没有声明该属性, 若直接实例化父类, 再调用这个方法会报错
print("欢迎来到游戏商店!")
class Skill():
def __init__(self,arm,energy):
self.arm = arm
self.energy = energy
def attack(self):
print("武器为%s"%self.arm)
class Shooter(Game,Shop): # 继承多个父类
def __init__(self,name,size,type="手游",price=0.00):
# 使用super()即把父类下的构造方法内的属性都放到子类中, 使得子类得以继承父类的属性
super().__init__(name,type,price)
# self.name = name
# self.type = type
# self.price = price
self.size = size
self.skill = Skill("m416",500) # 在类内实现另一个类的实例化, 即将别的类的实例作为该类的一个属性
def action(self):
print("欢迎来到{}游戏商店!".format(self.name))
# 类的实例化
# 注意 : 若指定传入参数可以不用按顺序进行写, 但必须全部都是写成按指定名字的形参进行传
# 像这样是错误的game1 = Shooter(size="100GB",绝地求生","端游")
# 不传参的就按默认值取
game1 = Shooter(size="100GB",name="绝地求生",type="端游")
game2 = Shooter("和平精英","5GB","手游")
game2.skill.attack()
导入类
- 一个模块就是一个
py
文件 - 可以将一个类写进一个模块里
- 一个模块中可以有多个类, 因此模块的文件名不要求首字母大写
导入单个类
-
使用
import
关键字导入 -
若从某个模块中导入一个类, 则用
from 模块名 import 类名
-
as
关键字可以给模块/类/函数指定别名- 如果要导入的模块/类/函数的名称可能与程序中现有的名称冲突,或者函数的名称太长,可指定简短而独一无二的 别名 —— 模块/类/函数的另一个名称,类似于外号。
- 要指定这种特殊外号,只需要在导入它后面使用
as
关键字 :import 模块/类/函数名 as 别名
或者from 模块名 import 类/函数名 as 别名
-
比如 :
from supermarket import GetMessage as gm
即从supermarket
模块中导入GetMessage
类, 将该类类名改为gm
p.s. 从模块中导入特定的方法(函数)也是用import
关键字, 即from 模块名 import 函数名
从一个模块中导入多个类
-
可根据需要在程序文件中导入任意数量的类
-
导入方法和导入单个类一样, 用
import
关键字, 用,
隔开 -
比如 :
from supermarket import Commodity,GetMessage,Pay
导入三个类 -
导入模块中的所有类 :
from 模块名 import *
- 不建议用这种方法导入
- 当类名很多时, 你将不知道你的程序用了哪些类
- 有可能导入了一个与程序文件中其他东西同名的类,引发难以察觉的错误
- 不建议用这种方法导入
-
也可以导入整个模块, 直接用
import 模块名
-
需要从一个模块中导入很多类时,最好导入整个模块
-
使用
模块名.类名
的方式来访问类
-
在一个模块中导入另一个模块
- 当一个模块中的类有依赖的类在另一个模块中, 就需要从另一个模块中导入所依赖的类到这个模块中
- 最后再将该模块的类导入到主程序中
Tips :
-
类名应采用 驼峰命名法 ,即将类名中的每个单词的首字母都大写,而不使用下划线。实例名和模块名都采用小写格式,并在单词之间加上下划线。
-
可使用空行来组织代码,但不要滥用。在类中,可使用一个空行来分隔方法;而在模块中,可使用两个空行来分隔类。
-
需要同时导入标准库中的模块和你编写的模块时,先编写导入标准库模块的 import 语句,再添加一个空行,然后编写导入你自己编写的模块的 import 语句。在包含多条 import 语句的程序中,这种做法让人更容易明白程序使用的各个模块都来自何方。
举个大栗子:
模仿超市购物, 将商品添加到购物车然后进行结账。
supermarket.py
文件
# 商品类
class Commodity():
def __init__(self,name,price=0.00):
#定义类的属性, 商品名称, 商品价格, 商品折扣
self.goodsName = name
self.price = price
self.discount=1.00
def setGoodsName(self,name):
self.goodsName = name
print("已将该商品命名为:{}",format(self.goodsName))
def setPrice(self,price):
self.price = price
print("该商品的价格已设为:{:.2f}元".format(self.price))
def setDiscount(self,discount):
self.discount = discount
print("该商品的折扣为{:.2f}".format(self.discount))
# 结账
class Pay():
def __init__(self):
self.count = 0.00
self.dic = {} # 存放商品的名的列表, key为商品名, num为数量
def add(self,name,count, num=1): # 添加商品
# self.dic.__setitem__(name,num) #设置字典的key和value
if num < 1:
return False
count = float(count)
self.dic.setdefault(name, num) #设置字典的key和value
now_count = count*num
self.count += now_count
print("添加到购物车的商品名为:{} 添加的金额为:{:.2f}".format(name,now_count))
def sub(self,name,count,num=1): # 删除商品
if num < 1:
return False
now_count = count*num
self.count -= now_count
self.dic.pop(name)
print("删除购物车的商品名为:{} 应减的金额为:{:.2f}".format(name, now_count))
def balance(self): # 结账
print("账单 : ")
print("商品名\t数量\t")
for items in self.dic.items():
print("{}\t{}\t".format(items[0],items[1]))
print("需要支付的总金额为:{:.2f}元".format(self.count))
message.py
文件
from supermarket import Commodity
# 当前模块中的类有依赖于supermarket模块中的Commodity类
# 获取商品信息类
class GetMessage(Commodity):
def __init__(self,name,price=0.00):
super().__init__(name,price) # 将父类的构造函数中的属性相互关联
def getName(self):
try:
# print("该商品的名称为:{}".format(self.goodsName))
return self.goodsName
except:
print("该商品并没有设置命名")
def getPrice(self):
try:
# print("该商品的价格为{:.2f}元".format(self.price))
return self.price
except:
print("该商品还没有设置价格")
def getDiscount(self):
# if self.discount == 1.00:
# print("该商品没有打折扣")
# else:
# print("该商品的折扣为{:.2f}".format(self.discount))
return self.discount
shopping.py
from message import GetMessage as gm
from supermarket import Commodity, Pay
# 添加商品信息
def goods():
apple = gm("苹果", 2)
orange = gm("橙子", 3)
banana = gm("香蕉", 1.5)
return [apple,orange,banana]
def goodsMessage(ls):
print("输出商品信息:")
print("商品名称\t商品价格\t商品折扣\t")
for item in ls:
print("{}\t{}\t\t{}\t".format(item.getName(),item.getPrice(),item.getDiscount()))
print("对应列表索引:")
num = 0
for item in ls:
print("{}\t{}".format(num,item.getName()))
num+=1
def buy(ls):
sum = Pay()
# sum.add(banana.goodsName, banana.price, 3)
while True:
a = input("请输入你要添加到购物车的索引:(输入q退出结账)")
if a == 'q':
break
try:
num = int(a)
b = int(input("请输入要购买的数量:"))
sum.add(ls[num].getName(), ls[num].getPrice(), b)
except:
print("输入有误,请重新输入")
continue
# 最后结账
sum.balance()
if __name__ == '__main__':
ls =goods()
goodsMessage(ls)
buy(ls)
运行效果 :
输出商品信息:
商品名称 商品价格 商品折扣
苹果 2 1.0
橙子 3 1.0
香蕉 1.5 1.0
对应列表索引:
0 苹果
1 橙子
2 香蕉
请输入你要添加到购物车的索引:(输入q退出结账)0
请输入要购买的数量:2
添加到购物车的商品名为:苹果 添加的金额为:4.00
请输入你要添加到购物车的索引:(输入q退出结账)1
请输入要购买的数量:3
添加到购物车的商品名为:橙子 添加的金额为:9.00
请输入你要添加到购物车的索引:(输入q退出结账)q
账单 :
商品名 数量
苹果 2
橙子 3
需要支付的总金额为:13.00元