Python面向对象编程
- 1.面向对象概念介绍
- 1) 面相过程 —— 怎么做?
- 2)面向对象 谁来做
- 2.类和对象
- 2.1类
- 2.2对象
- 2.3类和对象的关系
- 2.4类的设计
- 2.5面向对象设计案例 士兵类设计
- 2.6身份运算符
- 3.私有属性和私有方法
- 3.1. 应用场景及定义方式
- 4.继承、多态
- 重写
- 父类的私有属性和私有方法
- 多进程
- • 多态
- 5.类属性和类方法
- 5.1 术语—— 实例
- 5.21.2 类是一个特殊的对象
- 5.3. 类属性和实例属性
- 5.4. 类方法和静态方法
- 5.5静态方法
1.面向对象概念介绍
- 1.1 过程和函数(科普)
• 过程 是早期的一个编程概念
• 过程 类似于函数,只能执行,但是没有返回值
• 函数 不仅能执行,还可以返回结果 - 1.2 面相过程 和 面相对象 基本概念
1) 面相过程 —— 怎么做?
- 把完成某一个需求的 所有步骤 从头到尾 逐步实现
- 根据开发需求,将某些 功能独立 的代码 封装 成一个又一个 函数
- 最后完成的代码,就是顺序地调用 不同的函数
特点 - 注重 步骤与过程,不注重职责分工
- 如果需求复杂,代码会变得很复杂
- 开发复杂项目,没有固定的套路,开发难度很大!
2)面向对象 谁来做
相比较函数,面向对象 是 更大 的 封装,根据 职责 在 一个对象中 封装 多个方法
- 在完成某一个需求前,首先确定 职责 —— 要做的事情(方法)
- 根据 职责 确定不同的 对象,在 对象 内部封装不同的 方法(多个)
- 最后完成的代码,就是顺序地让 不同的对象 调用 不同的方法
特点 - 注重 对象和职责,不同的对象承担不同的职责
- 更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路
- 需要在面向过程基础上,再学习一些面向对象的语法
2.类和对象
- 类和对象的概念
类 和 对象 是 面向对象编程的 两个 核心概念
2.1类
• 类 是对一群具有 相同 特征 或者 行为 的事物的一个统称,是抽象的,不能直接使用
– 特征 被称为 属性
– 行为 被称为 方法
• 类 就相当于制造飞机时的图纸,是一个 模板,是 负责创建对象的
2.2对象
• 对象 是 由类创建出来的一个具体存在,可以直接使用
• 由 哪一个类 创建出来的 对象,就拥有在 哪一个类 中定义的:
2.3类和对象的关系
• 类是模板,对象 是根据 类 这个模板创建出来的,应该 先有类,再有对象
• 类 只有一个,而 对象 可以有很多个
– 不同的对象 之间 属性 可能会各不相同
• 类 中定义了什么 属性和方法,对象 中就有什么属性和方法,不可能多,也不可能少
2.4类的设计
在使用面相对象开发前,应该首先分析需求,确定一下,程序中需要包含哪些类!
在程序开发中,要设计一个类,通常需要满足一下三个要素:
1 类名 这类事物的名字,满足大驼峰命名法
2 属性 这类事物具有什么样的特征
3 方法 这类事物具有什么样的行为
-
驼峰命名法
CapWords
1 每一个单词的首字母大写
2 单词与单词之间没有下划线 -
3.1 类名的确定
名词提炼法 分析 整个业务流程,出现的 名词,通常就是找到的类 -
3.2 属性和方法的确定
• 对 对象的特征描述,通常可以定义成 属性
• 对象具有的行为(动词),通常可以定义成 方法
提示:需求中没有涉及的属性或者方法在设计类时,不需要考虑 -
实例
代码:
#!/usr/bin/python
# author X_Dragon
# E-mail:3270895551@qq.comclass Person:def __init__(self, name, age, height):self.name = nameself.age = ageself.height = heightdef introduce(self):print("我是:%s,今年%d岁,身高%.2f" % (self.name, self.age, self.height))def run(self):print("%s爱跑步,跑步锻炼身体" % self.name)def eat(self):print("%s是吃货,吃完再减肥" % self.name)xiaoming = Person("小明", 18, 180)
xiaoming.introduce()
xiaoming.run()
xiaoming.eat()
print(xiaoming)# 打印地址 0x0000015E2D92BFD0xiaomei=Person("小美",20,168)
xiaomei.introduce()
xiaomei.run()
xiaomei.eat()
print(xiaomei) # 0x0000015E2D92BE50
- 运行截图
2.5面向对象设计案例 士兵类设计
- 需求
- 代码
#!/usr/bin/python
# author X_Dragon
# E-mail:3270895551@qq.comclass Gun:def __init__(self,gun_model):# 枪的型号self.model=gun_model# 剩余子弹self.bullet_count=0# 默认0# 填充子弹def Add_bullet(self,count):self.bullet_count+=count# 射击def Shoot(self):if(self.bullet_count>0):self.bullet_count -= 1print("开始射击,剩余子弹:%d"%self.bullet_count)else:print("枪没有子弹了,请填充子弹")
# 创建枪的对象 AK47
ak47=Gun("ak47")
ak47.Add_bullet(50)
ak47.Shoot()# 开发士兵类
class Soldier:def __init__(self,name):self.name=nameself.gun=None# 默认新兵没枪def fire(self):if(self.gun is None):print("%s还没有枪...."%self.name)return# 到这一步 ,满足有枪的条件print("冲锋......我是:[%s]"%self.name)# 装子弹print("装子弹....")self.gun.Add_bullet(50)# 发射self.gun.Shoot()
# 创建实例
M4=Gun("M4")
XUSANDUO=Soldier("许三多")
XUSANDUO.gun=M4# 如果是新兵 就不配枪
XUSANDUO.fire()
- 运行:
- 小结:
2.6身份运算符
- is和==区别
3.私有属性和私有方法
3.1. 应用场景及定义方式
应用场景
• 在实际开发中,对象 的 某些属性或方法 可能只希望 在对象的内部被使用,而不希望在外部被访问到
• 私有属性 就是 对象 不希望公开的 属性
• 私有方法 就是 对象 不希望公开的 方法定义方式
• 在 定义属性或方法时,在 属性名或者方法名前 增加 两个下划线,定义的就是私有 属性或方法
在Python中,你可以将对象的属性设为私有属性,以防止直接访问和修改它们。要将属性设置为私有属性,通常在属性名称前面添加一个下划线前缀(单个下划线),这是一种约定,表示该属性是私有的,应该在类的内部使用,而不是在外部直接访问。虽然这不会强制阻止外部访问,但它是一种约定,告诉其他开发者应该将其视为私有属性。
下面是一个示例,演示如何在Python类中使用私有属性:
class Person:def __init__(self, name, age):self._name = name # 前面添加下划线,表示私有属性self._age = agedef introduce(self):print(f"我是{self._name},今年{self._age}岁。")def change_name(self, new_name):self._name = new_name # 类的内部可以修改私有属性# 创建对象
xiaoming = Person("小明", 20)# 访问私有属性
print(xiaoming._name) # 仍然可以访问,但不建议在外部直接访问# 通过对象方法修改私有属性
xiaoming.change_name("小红")# 再次访问私有属性
print(xiaoming._name) # 仍然可以访问,但不建议在外部直接访问
在上面的示例中,我们将 name 和 age 属性设为私有属性,添加了一个下划线前缀。虽然我们仍然可以在外部访问和修改这些属性,但是约定是在类的内部使用它们,而不要在外部直接访问。此外,我们还提供了一个对象方法 change_name,允许在类的内部修改私有属性的值。这种方法提供了更好的封装和控制,以防止意外的外部访问和修改。
- 运行
如图 ,哪怕是按_命名私有了,外部对象依旧可以访问私有,那么私有怎么体验呢?CHATGPD回答:
4.继承、多态
继承的概念:子类继承父类的方法和属性
- 继承语法
- 继承的语法
class 类名(父类名): pass
• 子类 继承自 父类,可以直接 享受 父类中已经封装好的方法,不需要再次开发
• 子类 中应该根据 职责,封装 子类特有的属性和方法 - 专业术语
• Dog 类是Animal 类的子类,Animal 类是Dog 类的父类,Dog 类从Animal 类继承
• Dog 类是Animal 类的派生类,Animal 类是Dog 类的基类,Dog 类从Animal 类派生 - 继承的传递性
• C 类从B 类继承,B 类又从A 类继承
• 那么C 类就具有B 类和A 类的所有属性和方法
子类 拥有 父类 以及 父类的父类 中封装的所有 属性 和 方法
- 实例
#!/usr/bin/python
# author X_Dragon
# E-mail:3270895551@qq.com
class Animal:def eat(self):print("吃-------")def sleep(self):print("睡-------")class Dog(Animal):# 狗叫def bark(self):print("汪汪旺-------")d=Dog()
d.bark()
d.sleep()
d.eat()
- 运行
可见 dog继承了Animal的eat、sleep类
重写
- 重写 父类方法有两种情况:
1 覆盖 父类的方法
2 对父类方法进行 扩展
- 覆盖父类的方法
• 如果在开发中,父类的方法实现 和 子类的方法实现,完全不同
• 就可以使用 覆盖 的方式,在子类中重新编写 父类的方法实现
具体的实现方式,就相当于在 子类中 定义了一个 和父类同名的方法并且实现
- 重写之后,在运行时,只会调用 子类中重写的方法,而不再会调用 父类封装的方法
-
- 对父类方法进行扩展
• 如果在开发中,子类的方法实现 中 包含父类的方法实现
– 父类原本封装的方法实现 是 子类方法的一部分
• 就可以使用 扩展 的方式
a) 在子类中重写 父类的方法
b) 在需要的位置使用super().父类方法来调用父类方法的执行
c) 代码其他的位置针对子类的需求,编写 子类特有的代码实现
- 对父类方法进行扩展
- 关于super
• 在Python 中super 是一个 特殊的类
• super() 就是使用super 类创建出来的对象
• 最常 使用的场景就是在 重写父类方法时,调用 在父类中封装的方法实现 - 实例
#!/usr/bin/python
# author X_Dragon
# E-mail:3270895551@qq.com
class Animal:def eat(self):print("吃-------")def sleep(self):print("睡-------")class Dog(Animal):# 狗叫def bark(self):print("汪汪旺-------")class xiaotianquan(Dog):def fly(self):print("我会飞........")# 重写 barkdef bark(self):print("哮天犬在咆哮........")xtq=xiaotianquan()
xtq.bark()
xtq.fly()
xtq.sleep()
xtq.eat()
上面实例 哮天犬就把dog类的bark重写了
父类的私有属性和私有方法
1 子类对象不能 在自己的方法内部,直接 访问父类的 私有属性 或 私有方法
2 子类对象 可以通过 父类 的 公有方法间接 访问到 私有属性 或 私有方法(父类在自己的公有方法中调用私有方法)
• 私有属性、方法 是对象的隐私,不对外公开,外界 以及 子类 都不能直接访问
• 私有属性、方法 通常用于做一些内部的事情
示例
B的对象不能直接访问 num2 属性
• B 的对象不能在demo 方法内访问 num2 属性
• B 的对象可以在demo 方法内,调用父类的test 方法
• 父类的test 方法内部,能够访问 num2 属性和 test 方法
测试代码:
class A:def __int__(self):self.num1=100self._num2=200def _test(self):print("这是一个内部方法")
class B(A):def demo(self):pass
b=B()
print(b)
print(b._test())
print(b._num2)
继承规则:
运行结果:
多进程
- Python 中的MRO —— 方法搜索顺序(知道)
• Python 中针对 类 提供了一个 内置属性 mro 可以查看 方法 搜索顺序
• MRO 是method resolution order,主要用于 在多继承时判断方法、属性的调用路径
print(C. mro )输出结果
(<class ’ main .C’>, <class ’ main .A’>, <class ’ main .B’>, <cla ss ‘object’>)
• 在搜索方法时,是按照 mro 的输出结果 从左至右 的顺序查找的
• 如果在当前类中 找到方法,就直接执行,不再搜索
• 如果 没有找到,就查找下一个类 中是否有对应的方法,如果找到,就直接执行,不再搜索
• 如果找到最后一个类,还没有找到方法,程序报错
- 2.2 新式类与旧式(经典)类
object 是Python 为所有对象提供的 基类,提供有一些内置的属性和方法,可以使用dir 函数查看
• 新式类:以object 为基类的类,推荐使用
• 经典类:不以object 为基类的类,不推荐使用
• 在Python 3.x 中定义类时,如果没有指定父类,会 默认使用 object 作为该类的 基类 —— Python 3.x 中定义的类都是 新式类
• 在Python 2.x 中定义类时,如果没有指定父类,则不会以object 作为 基类
新式类 和 经典类 在多继承时—— 会影响到方法的搜索顺序
为了保证编写的代码能够同时在Python 2.x 和Python 3.x 运行! 今后在定义类时,如果没有父类,建议统一继承自object
class 类名(object): pass
Python 至少有三种不同的 MRO:
而这个 MRO 列表的构造是通过一个 C3 线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的 MRO 列表并遵循如下三条准则:
- 子类会先于父类被检查
- 多个父类会根据它们在列表中的顺序被检查
- 如果对下一个类存在两个合法的选择,选择第一个父类
• 多态
面向对象三大特性
1 封装 根据 职责 将 属性 和 方法封装 到一个抽象的 类 中
– 定义类的准则
2 继承实现代码的重用,相同的代码不需要重复的编写
– 设计类的技巧
– 子类针对自己特有的需求,编写特定的代码
3 多态 不同的 子类对象 调用相同的 父类方法,产生不同的执行结果
– 多态 可以 增加代码的灵活度
– 以 继承 和 重写父类方法 为前提
– 是调用方法的技巧,不会影响到类的内部设计单一职责,开放 封闭—对扩展开放,对修改封闭
- 演示
需求
1 在Dog 类中封装方法game
– 普通狗只是简单的玩耍
2 定义XiaoTianDog 继承自Dog,并且重写game 方法
– 哮天犬需要在天上玩耍
3 定义Person 类,并且封装一个 和狗玩 的方法
– 在方法内部,直接让 狗对象 调用game 方法
#!/usr/bin/python
# author X_Dragon
# E-mail:3270895551@qq.com
class Dog:def __init__(self,name):self.name=namedef game(self):print("普通狗 普通玩耍 在地面哇哇哇....")
class XIAOTIANQUAN(Dog):def game(self):print("我是哮天犬,我在天上玩耍,芜湖.....")
class Person:def __init__(self,name):self.name=namedef game(self,dog):print("和%s快乐玩耍"%dog.name)wangcai=Dog("旺财")
XTQ=XIAOTIANQUAN("哮天犬")xiaoming=Person("小明")xiaoming.game(XTQ)
运行:
5.类属性和类方法
5.1 术语—— 实例
1 使用面向对象开发,第 1 步 是设计 类
2 使用 类名() 创建对象,创建对象 的动作有两步:
– 1) 在内存中为对象 分配空间
– 2) 调用初始化方法 init 为 对象初始化
3 对象创建后,内存 中就有了一个对象的 实实在在 的存在—— 实例
因此,通常也会把:
1 创建出来的 对象 叫做 类 的 实例
2 创建对象的 动作 叫做 实例化
3 对象的属性 叫做 实例属性
4 对象调用的方法 叫做 实例方法
在程序执行时:
1 对象各自拥有自己的 实例属性
2 调用对象方法,可以通过self.
– 访问自己的属性
– 调用自己的方法
- 结论
每一个对象 都有自己 独立的内存空间,保存各自不同的属性
• 多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用 传递到方法内部(知道为啥要搞 self 了吧
5.21.2 类是一个特殊的对象
Python 中 一切皆对象:
• class AAA: 定义的类属于 类对象
• obj1 = AAA() 属于 实例对象
• 在程序运行时,类 同样 会被加载到内存
• 在Python 中,类 是一个特殊的对象—— 类对象
• 在程序运行时,类对象 在内存中 只有一份,使用 一个类 可以创建出 很多个对象实例
• 除了封装 实例 的 属性 和 方法外,类对象 还可以拥有自己的 属性 和 方法
d) 类属性
e) 类方法
• 通过 类名. 的方式可以 访问类的属性 或者 调用类的方法
5.3. 类属性和实例属性
- 概念和使用
• 类属性 就是给 类对象 中定义的 属性
• 通常用来记录 与这个类相关 的特征
• 类属性不会用于记录 具体对象的特征
5.4. 类方法和静态方法
- 类方法
类属性 就是针对 类对象 定义的属性
– 使用 赋值语句 在class 关键字下方可以定义 类属性
– 类属性 用于记录 与这个类相关 的特征
• 类方法 就是针对 类对象 定义的方法
– 在 类方法 内部可以直接访问 类属性 或者调用其他的 类方法语法如下
@classmethod
def 类方法名(cls): pass
• 类方法需要用 修饰器 @classmethod 来标识,告诉解释器这是一个类方法
• 类方法的 第一个参数 应该是cls
– 由 哪一个类 调用的方法,方法内的cls 就是 哪一个类的引用
– 这个参数和 实例方法 的第一个参数是self 类似
– 提示 使用其他名称也可以,不过习惯使用cls
• 通过 类名. 调用 类方法,调用方法时,不需要传递cls 参数
• 在方法内部
– 可以通过cls. 访问类的属性
– 也可以通过cls. 调用其他的类方法示例需求
5.5静态方法
• 在开发时,如果需要在 类 中封装一个方法,这个方法:
– 既 不需要 访问 实例属性 或者调用 实例方法
– 也 不需要 访问 类属性 或者调用 类方法
• 这个时候,可以把这个方法封装成一个 静态方法,例如打印一些帮助语法如下
@staticmethod
def 静态方法名(): pass
• 静态方法 需要用 修饰器 @staticmethod 来标识,告诉解释器这是一个静态方法
• 通过 类名. 调用 静态方法
创作不易 点赞发财~~~~