面向对象
一、面向对象基本概念
1、面向对象编程:oop
2、面向过程:根据需求将某些独立功能封装为一个又一个函数,最后完成的代码,就是顺序地调用不同函数
3、面向对象:相比较函数,面向对象就是更大的封装,根据职责在一个对象中封装多个方法,根据职责确定不同对象,在对象内部封装不同方法。
二、类 和 对象(面向对象中最重要的两个内容)
1、类:是对具有相同特征或者行为的事物的一个统称,是抽象的,不能直接使用。(指代多个事物,由 class 关键字定义)
特征(静态)--- 属性
行为(动态)--- 方法
2、对象:是由类创建出来的一个具体存在的事物,可以直接使用。(指代一个具体事物,代码中使用 类 区创建实例化)
是由哪一个类创建出来的对象,就拥有在哪一个类中定义的属性和方法
3、类的构成
类的三要素:
类名(多个事物起的名字,满足大驼峰命名法,所有单词首字母大写)
属性(事物的特征)
方法(事物的行为)
三、面向对象代码的步骤
1、设计类(找类的三要素)
2、定义类
3、创建对象(实例化对象)
4、由对象调用类中方法
四、类的设计
1、类的设计:就是找三要素,属性、方法 可能会很多,需要找到关注的即可
2、类名的提取:使用名词提炼法分析整个业务流程,得出的名词通常就是类名
五、面向对象的基本语法
1、定义类
class Person: #Person为类名 def func(self): #func为方法名,本质为函数 pass
2、创建对象(实例化对象)
在代码中,对象由类对象
类名() #就是创建对象,一般使用变量将创建的对象保存起来
变量 = 类名() 一般将这个变量称为对象,变量中保存的是对象的引用地址
3、调用类中的方法
由类创建的对象,可调用类中的方法
对象 . 方法名()
class Cat: def eat(self): print("小猫爱吃鱼") def drink(self): print("小猫要喝水") #创建对象并实例化 tom = Cat() #以变量接收对象 #调用 tom.eat() tom.drink()
六、属性和方法
1、属性的使用
属性表示事物特征
可以给对象添加属性,或者获取对象属性值
给对象添加属性:对象 . 属性名 = 属性值 #添加或者修改
获取对象属性值:对象 . 属性名
方法中添加属性名: self . 属性名 = 属性值
例子:在外部添加属性名
class Cat: def eat(self): print(f"{self.name}小猫爱吃鱼") def drink(self): print(f"{self.name}小猫要喝水") #创建对象并实例化 tom = Cat() #以变量接收对象 tom.name = '唐木木' #给tom对象添加属性值 #调用 tom.eat() tom.drink()
2、属性(魔法方法)
在Python中存在一类方法,以两个下划线开头,两个下划线结尾,在满足某个条件的情况下,会自动调用。这类方法称为魔法方法
1、初始化方法:__init__
调用时机:创建对象后会自动调用
应用场景:初始化对象,给对象添加属性
例子
class Cat: def __init__(self): print("我是init,我已经调用") self.name = "Tom" def eat(self): print(f"{self.name}小猫爱吃鱼") def drink(self): print(f"{self.name}小猫要喝水") #创建对象并实例化 tom = Cat() #以变量接收对象,此时执行init方法
b = tom #此时不会执行init,这只是引用,不是创建对象####只有加括号的才是创建对象
2、__str__ 方法
调用方法:使用print(对象),打印对象的时候,会自动调用
如果没有 str 方法,默认打印的是对象的引用地址
如果定义 str 方法,打印的是方法的返回值
应用场景:使用print(对象),打印输出对象的属性信息
注意:必须返回一个 字符串
例子
class Cat: def __init__(self,name,age): #创建添加属性 self.name = name self.age = age def __str__(self): return f"姓名:{self.name},年龄:{self.age}" #创建对象并实例化 tom = Cat("Tom",12) #以变量接收对象,此时执行init方法 print(tom) #<__main__.Cat object at 0x0000012EDDBF92B0>(无str方法时) #姓名:Tom,年龄:12 (有str方法时)
3、dir() 函数
可以查看对象内的 所有属性及方法
不具有输出功能,需要配合 print 连用
class Cat: def __init__(self, name, age): # 创建添加属性 self.name = name self.age = age def eat(self): print(f"{self.name}小猫爱吃鱼") def drink(self): print(f"{self.name}小猫要喝水") if __name__ == "__main__": #创建对象并实例化 tom = Cat("Tom",12) #以变量接收对象,此时执行init方法 print(dir(tom)) #<__main__.Cat object at 0x0000012EDDBF92B0>(无str方法时) #姓名:Tom,年龄:12 (有str方法时)
七、面向对象的案例
封装案例
class Person: def __init__(self, name, weight): # 创建添加属性 self.name = name self.weight = weight def __str__(self): return f"{self.name} 目前体重为 {self.weight}" def run(self): #体重减少0.5kg self.weight -= 0.5 print(f"{self.name} 跑步了,体重减少0.5kg") def eat(self): #体重增加1kg self.weight += 1 print(f"{self.name} 跑步了,体重增加1kg") if __name__ == "__main__": #创建对象并实例化 xiaoming = Person("小明",12) xiaomei = Person("小美",30) print(xiaoming) print(xiaomei) xiaomei.run() xiaoming.eat()
家具、房子案例
class HouseItem: '''家具类''' def __init__(self, name, area): self.name = name self.area = area def __str__(self): return f"{self.name} 占地面积 {self.area} 平方米" class House: '''房子类''' def __init__(self,h_type,area): self.h_type = h_type self.area = area self.free_area = area self.item_list = [] #设置为空 def __str__(self): return f"户型:{self.h_type} 面积:{self.area} 剩余面积:{self.free_area} 家具名称列表:{self.item_list}" def add_item(self,item): #添加房子对象,添加家居对象 #item为家具对象 if self.free_area > item.area: #将家居类的对象属性获取 print(f"添加家具{item.name}") self.item_list.append(item.name) #修改剩余面积 self.free_area -= item.area else: print("房子剩余面积不足") if __name__ == "__main__": #创建家具 bed = HouseItem("席梦思",4) chest = HouseItem("衣柜",2) print(bed) print(chest) #创建房子 house = House("三室一厅",100) print(house) #添加家具 house.add_item(bed) print(house) house.add_item(chest) print(house)
八、面向对象的三大特征(继承、封装、多态)
1、继承:如果多个类中存在相同的代码逻辑,则可考虑将相同逻辑的代码封装到父类中,在通过继承关系。直接实例化子类对象并调用父类中的方法即可。避免反复编写相同逻辑代码。
在继承关系中,子类可以拥有并直接使用父类所有方法和属性
案例:(继承具有传递性)(先在自己类中找,找到直接使用,没找到就去父类找,父类没找到就去父类的父类找。以下往上。直到找到object父类)
class Animal: '''动物类''' def __init__(self,name,age): self.name = name self.age = age def eat(self): print(f"{self.name} 在吃饭") def sleep(self): print(f"{self.name} 在睡觉") class Cat(Animal): #此时继承Animal,Animal为父类 '''猫类''' def catch(self): print(f"{self.name} 会抓老鼠") class Dog(Animal): '''狗类''' def look_for_door(self): print(f"{self.name} 正在看门") class XTQ(Dog): '''哮天犬''' def fly(self): print(f"{self.name} 在飞") if __name__ == "__main__": ani = Animal("佩奇",3) ani.eat() #佩奇 在吃饭 cat = Cat("黑猫",4) #此时Cat继承了Animal则可以使用父类的方法。如下 cat.eat() #黑猫 在吃饭 cat.sleep() #黑猫 在睡觉 cat.catch() #调用自己的方法。黑猫 会抓老鼠 dog = Dog("旺财",5) dog.eat() xtq = XTQ("哮天犬",100) #继承了Animal,Dog可同时使用他们的方法 xtq.look_for_door() xtq.eat()
2、重写
定义:在子类中定义了和父类中一样的方法
为什么要重写:父类中的方法不能满足子类对象的需要
方式:
覆盖式重写:父类中的代码功能全部不要(直接在子类中定义和父类中方法名字一样的方法接口,直接重新写新的代码)
class Dog: def bark(self): print("汪汪叫") class XTQ(Dog): '''覆盖父类方法''' def bark(self): print("嗷嗷叫") if __name__ == "__main__": xtq = XTQ() xtq.bark() #嗷嗷叫
扩展式重写:保留父类中的功能,在此基础上添加新功能(在子类代码中使用 super() . 方法名() 调用父类中的新功能,然后再写新功能)
class Dog: def bark(self): print("汪汪叫") class XTQ(Dog): '''覆盖父类方法''' def bark(self): #调用父类功能 super().bark() #先调用父类中的方法 print("嗷嗷叫") if __name__ == "__main__": xtq = XTQ() xtq.bark() # 先 汪汪叫 再 嗷嗷叫
3、多态
不同的子类对象调用相同的方法,产生不同的执行结果
可以增加代码的灵活度,以继承和重写父类方法为前提,是调用方法的技巧,不会影响类的内部设计
案例
class XTQ(Dog): def game(self): print("哮天犬天上玩") class Person: def play_with_dog(self,dog): '''dog 是狗类或者其子类的对象''' print("人和狗一起玩") dog.game() #使用上面类的方法 if __name__ == "__main__": #实例化对象 dog1 = Dog() xtq = XTQ() xiaowang = Person() xiaowang.play_with_dog(dog1) #先 人和狗一起玩 再 简单的玩啥 xiaowang.play_with_dog(xtq) #先 人和狗一起玩 再 哮天犬天上玩
九、私有和公有
在Python中,定义类的时候,可以给 属性和方法 设置访问权限,即规定在什么地方使用 该权限分为两种:公有和私有
1、公有
定义:直接定义的属性和方法,就是公有的。可以在任何地方访问和使用。只要有对象就可以访问和使用
2、私有
定义:只能在类内部定义 (class 关键字的缩写中)。只需要在属性或者方法名前面加上两个下划线,就会变成私有。只能在当前类的内部使用。不能在外部和子类中使用
应用场景:
一般定义的属性和方法都为公有。当某个属性和方法不想被外界直接使用,就定义为私有。
class Person: def __init__(self,name,age): self.name = name self.__age = age def __str__(self): return f"{self.name},{self.__age}" def set_age(self,age): #定义公有方法去修改私有方法 self.__age = age if __name__ == "__main__": #实例化对象 xw = Person("小王",20) print(xw) #小王,20 xw.age = 100 print(xw) #小王,100 #设为私有后(在age前面加__) xw.age = 100 print(xw) #小王,20 此时就无法使用age ,不能改变 #修改 xw.set_age(10000) print(xw) #小王,10000 通过类里面的公有方法修改私有方法
十、对象划分
Python中一切皆对象
1、类对象:就是类。在代码执行的时候,解释器会自动创建,
作用:使用类对象创建实例化对象。存储一些类的特征值(类属性)
2、实例化对象:创建对象也称为实例化,由类对象创建的对象 称为实例对象,简称实例。(类名 ())
作用:保存实例的特征值(实例属性)
十一、属性的划分
1、实例属性
是每个实例对象 具有的特征(属性)
定义:一般都是在 init 方法中,使用 self .属性名 = 属性值
特征:每个实例对象 都会保存自己的实例属性 即内存中存在多份
访问和修改:实例对象 . 属性 = 属性值 (修改) 实例对象 . 属性(访问)
2、类属性
是类对象具有的特征,是整个类的特征
定义:一般在类里面(class 缩进中),方法 def 外部 定义的变量
特征:只有类对象保存一份,即在内存中只有一个
访问和修改:类对象 . 属性 = 属性值 (修改) 类对象 . 属性(访问) #类对象就是类名
class Tool: #定义类属性 count = 0 def __init__(self,name): self.name = name #实例属性 Tool.count += 1 #每执行一次,计数一次对象数 if __name__ == "__main__": #查看创建对象的个数 print(Tool.count) #查看类属性 0 tool1 = Tool("锤子") print(Tool.count) # 1
十三、方法的划分
1、实例的方法
定义:如果方法中 需要使用 实例属性,则这个方法 必须 定义为实例方法
调用:实例对象 . 方法名()
class Tool: def bark(self): pass
2、类方法
定义:如果方法中 不需要使用 实例属性,但需要使用类属性 , 则这个方法可以定义为类方法(即在方法上方加@classmethod)
调用:类名 . 方法名() 或者 实例对象 . 方法名()
class Tool: @classmethod def bark(cls): pass
3、静态方法(了解)
定义:在方法中既不使用 实例属性,也不使用类属性 可以将该方法定义为静态方法(需要使用装饰器@staticmethod)
调用:类名 . 方法名() 或者 实例对象 . 方法名()
class Tool: @staticmethod def bark(): pass
例子:类方法调用
class Tool: #定义类属性 count = 0 def __init__(self,name): self.name = name #实例属性 Tool.count += 1 #每执行一次,计数一次对象数 @classmethod def show_tool_count(cls): return cls.count if __name__ == "__main__": #查看创建对象的个数 print(Tool.show_tool_count()) #通过类对象调用 0 tool1 = Tool("锤子") tool2 = Tool("刀子") print(tool2.show_tool_count()) #通过实例对象调用 2
案例:总结
import random class Game: #定义类属性,保存历史最高分 top_score = 0 def __init__(self,name): self.paly_name = name #实例属性 #静态方法 @staticmethod def show_help(): print("游戏的帮助信息") #类方法 @classmethod def show_top_score(cls): print(f"历史最高分为{cls.top_score}") def start_game(self): score = random.randint(10,100) print(f"玩家{self.paly_name} 本次得分 {score}") if score > Game.top_score: Game.top_score = score if __name__ == "__main__": Game.show_help() Game.show_top_score() player = Game("小王") player.start_game() ''' 游戏的帮助信息 历史最高分为0 玩家小王 本次得分 27 '''