> 文档中心 > 【Python中的面向对象】

【Python中的面向对象】


Python中的面向对象

文章目录

  • Python中的面向对象
      • 创建类
      • 使用类
      • 类的专有方法
    • 继承
      • 子类的构造方法
      • 重写父类的方法
      • 将实例用作属性
    • 导入类
      • 导入单个类
      • 从一个模块中导入多个类
      • 在一个模块中导入另一个模块

面向对象编程是最有效的编程方法之一,在C++和Java,JavaScript等编程语言中也都支持面向对象编程, 当然python也不例外。

类是创建对象的“模板”,编写类时,可以定义一大类对象都有的通用行为。基于类创建对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个性。

  • 实例化 : 根据类来创建对象

创建类

python2.xpython3.x创建类的过程有些细微的不同, 以下主要是以python3.x的语法为主

  • 关键字 : 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:0capacity:5introduce:车的简介修改后的属性值num:1capacity:3introduce:这是个牛逼的车

类的专有方法

专有方法 说明
__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 = priceclass 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 = priceclass 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()

导入类

当不断给类增加新功能时, 文件会逐渐开始变得庞大, 不利于维护和代码的工整,这与python最初的简洁理念相违背的,python就是为了简洁工整,而严格使用缩进来规范代码逻辑;因此python允许将类存储在模块中,然后可以在主程序中导入所需的模块,这样的好处是使代码尽可能简洁。

  • 一个模块就是一个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 *

    • 不建议用这种方法导入
      1. 当类名很多时, 你将不知道你的程序用了哪些类
      2. 有可能导入了一个与程序文件中其他东西同名的类,引发难以察觉的错误
  • 也可以导入整个模块, 直接用import 模块名

    • 需要从一个模块中导入很多类时,最好导入整个模块

    • 使用模块名.类名的方式来访问类

      可以避免了导入模块中的每个类可能引发的名称冲突。这个与导入类的所有模块是有区别的, 即使是类名相同, 但是导入整个模块的方式是通过模块名.类名来访问类, 就不会产生命名冲突, 而导入所有类的方式就只能以类名的方式进行访问, 这样就容易产生冲突。

      举个例子: 假设有一个fruit.py文件, 有多个类, 其中想访问Apple类

      # 导入 fruit 整个模块import fruita = fruit.Apple()# 导入 fruit 模块中的所有类from fruit import *a = Apple()

在一个模块中导入另一个模块

有时候,需要将类分散到多个模块中,以免模块太大,或在同一个模块中存储不相关的类。

将类存储在多个模块中时,你可能会发现一个模块中的类依赖于另一个模块中的类。

在这种情况下,可在前一个模块中导入必要的类。

  • 当一个模块中的类有依赖的类在另一个模块中, 就需要从另一个模块中导入所依赖的类到这个模块中
  • 最后再将该模块的类导入到主程序中

Tips :

  1. 类名应采用 驼峰命名法 ,即将类名中的每个单词的首字母都大写,而不使用下划线。实例名和模块名都采用小写格式,并在单词之间加上下划线。

  2. 可使用空行来组织代码,但不要滥用。在类中,可使用一个空行来分隔方法;而在模块中,可使用两个空行来分隔类。

  3. 需要同时导入标准库中的模块和你编写的模块时,先编写导入标准库模块的 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 gmfrom 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+=1def 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)

tips : 从shopping.py文件开始运行, 这三个py文件要放在同个文件夹下, 这样系统才能顺利找到模块的路径

运行效果 :

输出商品信息:商品名称商品价格商品折扣苹果    21.0橙子 31.0香蕉 1.51.0对应列表索引:0苹果1橙子2香蕉请输入你要添加到购物车的索引:(输入q退出结账)0请输入要购买的数量:2添加到购物车的商品名为:苹果 添加的金额为:4.00请输入你要添加到购物车的索引:(输入q退出结账)1请输入要购买的数量:3添加到购物车的商品名为:橙子 添加的金额为:9.00请输入你要添加到购物车的索引:(输入q退出结账)q账单 : 商品名数量苹果    2橙子 3需要支付的总金额为:13.00元

参考资料 :

   《Python 编程:从入门到实践》[ 美 ] Eric Matthes 译者:袁国忠

【Python中的面向对象】【Python中的面向对象】 创作挑战赛 【Python中的面向对象】 新人创作奖励来咯,坚持创作打卡瓜分现金大奖