Python之“面向对象”
一、两大编程思想

1、面向过程:
(1)特点:
以过程为中心,程序由一系列步骤或函数组成,依次执行。重点在于解决问题的步骤和方法。
(2)思想:
问题被分解成一系列步骤,通过控制流程和数据流程来解决问题。
(3)实例:
C语言是典型的面向过程编程语言,程序由一系列函数组成,函数之间通过参数传递数据。
2、面向对象:
(1)特点:
以对象为中心,程序由对象的定义、创建和交互组成。重点在于对象的行为和属性。
(2)思想 :
将问题看作是一组相互作用的对象,对象之间通过消息传递来交互。
(3)实例:
Java、C++和Python等语言是面向对象编程语言,程序由类和对象组成,类定义了对象的属性和行为。
3、两者的共同点:
(1)都是一种编程范式,用于组织和管理代码。
(2)都可以用来解决问题,但侧重点和方法不同。
(3)都可以使用流程控制和数据结构来实现程序逻辑。
4、两者的区别:
(1)面向过程侧重于解决问题的步骤和方法,强调数据和操作之间的关系;
面向对象侧重于对象的行为和属性,强调对象之间的交互和组合。
(2)面向过程中数据和函数是分离的,函数是对数据的操作;
面向对象中数据和函数被封装在对象中,对象是数据和函数的集合。
(3)面向对象更加灵活和易于扩展,可以通过继承、封装和多态等机制实现代码的复用和扩展;
面向过程相对简单直接,适用于一些简单的问题和场景。
二、类和对象
1、语法结构:
class 类名():
pass
对象名 = 类名()
2、组成:
(1)类属性:
直接定义在类中、方法外的变量
(2)实例属性:
定义在__init__方法中,使用 self 打点的变量
(3)实例方法:
定义在类中的函数,而且自带参数 self
(4)静态方法:
使用装饰器 @staticmethod 修饰的方法
(5)类方法:
使用装饰器 @classmethod 修饰的方法
class student:
# 类属性
school = 'b站'
# 初始化方法
def __init__(self,name,age):
self.name = name
self.age = age
# 实例方法
def show(self):
print(f'我叫:{self.name},今年:{self.age}岁了')
# 静态方法
@staticmethod
def sm():
print('这是一个静态方法。既不能调用实例属性,也不能调用实例方法。')
# 类方法
@classmethod
def cm(cls): # cls --> class 的缩写
print('这是一个类方法。既不能调用实例属性,也不能调用实例方法。')
# 创建类的对象
stu = student('wjk',18) # 此处需要传入 __init__ 中定义的两个形参
# 调用实例属性 --> 对象名.
print(stu.name,stu.age)
# 输出:wjk 18
# 调用类属性 --> 类名.
print(student.school)
# 输出:b站
# 调用实例方法 --> 对象名.
stu.show()
# 输出:我叫:wjk,今年:18岁了
# 调用类方法 --> 类名.
student.cm()
# 输出:这是一个类方法。既不能调用实例属性,也不能调用实例方法。
# 调用静态方法 --> 类名.
student.sm()
# 输出:这是一个静态方法。既不能调用实例属性,也不能调用实例方法。
3、动态绑定:
class student:
# 初始化方法
def __init__(self,name,age):
self.name = name
self.age = age
stu1 = student('wjk',18)
stu2 = student('wy',20)
# 动态绑定属性
stu1.gender = '男'
print(stu1.gender)
# 输出:男
# print(stu2.gender)
# 输出:AttributeError: 'student' object has no attribute 'gender'
# 动态绑定方法
def introduce():
print('我被stu1动态绑定了')
stu1.i = introduce
stu1.i()
# 输出:我被stu1动态绑定了
# stu2.i()
# 输出:AttributeError: 'student' object has no attribute 'i'
三、三大特征
1、封装:
(1)定义:
隐藏内部细节,对外提供操作方式
(2)权限控制:
1. 定义:
是通过对属性或方法添加单下划线、双下划线以及首尾双下划线来实现
2. 类型:
- 单下划线开头:表示 protected 受保护的成员,这类成员被视为仅供内部使用,允许类本身和子类进行访问,但实际上它可以被外部代码访问。
- 双下划线开头:表示 private 私有的成员,这类成员只允许定义该属性或方法的类本身进行访问。
- 首尾双下划线:一般表示特殊的方法
class student():
# 首尾双下划线
def __init__(self,name,age,gender):
self._name = name # 受保护的,只能由本身和子类访问
self.__age = age # 私有的,只能由本身访问
self.gender = gender # 实例属性,本身、子类、外部均可以访问
def _fun1(self): # 受保护的
print('本身和子类访问')
def __fun2(self): # 私有的
print('本身访问')
def show(self): # 实例方法
self._fun1() # 类本身访问受保护的方法
self.__fun2() # 类本身访问私有的方法
print(self._name) # 类本身访问受保护的属性
print(self.__age) # 类本身访问私有的属性
@property # 作用:修改方法,将方法转成属性使用
def age(self):
return self.__age
@age.setter # 作用:将 gender 这个属性设置为可写属性
def age(self,value):
if value < 0:
print('年龄有误,已默认设为18')
self.__age = 18 # 设置默认值
else:
self.__age = value
# 创建一个学生类
stu = student('wjk',18,'男')
# 类外部
print(stu._name) #受保护的
# 输出:wjk
# print(stu.__age) # 私有的
# 输出:AttributeError: 'student' object has no attribute '__age'. Did you mean: '_name'?
stu._fun1() # 受保护的
# 输出:本身和子类访问
# stu.__fun2() # 私有的
# 输出:AttributeError: 'student' object has no attribute '__fun2'. Did you mean: '_fun1'?
# 可访问的属性和方法
print(dir(stu))
# 输出:['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_fun1', '_name', '_student__age', '_student__fun2', 'gender', 'show']
# 访问私有属性和方法的形式
# (1)强制访问(不建议)
print(stu._student__age)
# 输出:18
stu._student__fun2()
# 输出:本身访问
# (2)装饰器 @property
print(stu.age)
# 输出:18
# 但是,该方法只能查看而不能修改属性值即需要在设置一个装饰器
# stu.age = 20
# 输出:AttributeError: property 'age' of 'student' object has no setter
stu.age = 20
print(stu.age)
# 输出:20
2、继承
(1)特点:
- 一个子类可以继承 N 多个父类
- 一个父类也可以拥有 N 多个子类
- 如果一个类没有继承任何类,那么这个类默认继承的是 object 类
(2)语法结构:
class 类名(父类1,父类2,...,父类N)
pass
# (1)多个子类,一个父类
# 创建父类
class person():
def __init__(self,name,age):
self.name = name
self.age = age
def show(self):
print(f'大家好,我叫{self.name},我今年{self.age}岁了')
# 创建子类
class student(person):
def __init__(self,name,age,stuno):
super().__init__(name,age) # 调用父类的初始化方法
self.stuno = stuno
class doctor(person):
def __init__(self,name,age,department):
super().__init__(name,age)
self.department = department
# 创建子类对象
stu = student('wjk',18,1001)
stu.show()
# 输出:大家好,我叫wjk,我今年18岁了
doc = doctor('yyqx',20,'外科')
doc.show()
# 输出:大家好,我叫yyqx,我今年20岁了
# (2)一个子类,多个父类
# 创建父类
class father1():
def __init__(self,name):
self.name = name
def show1(self):
print('父类1中的方法')
class father2():
def __init__(self,age):
self.age = age
def show2(self):
print('父类2中的方法')
# 创建子类
class son(father1,father2):
def __init__(self,name,age,gender):
# 调用父类的初始化方法
father1.__init__(self,name)
father2.__init__(self,age)
self.gender = gender
# 创建对象
s = son('wjk',18,'男')
s.show1()
# 输出:父类1中的方法
s.show2()
# 输出:父类2中的方法
(3)方法重写
1.特点:
- 子类继承了父类就拥有了父类中公有成员和受保护的成员
- 父类的方法不能完全适合子类的需求时,子类就可以重写父类的方法
- 子类在重写父类的方法时,要求方法的名称必须与父类方法的名称相同,在子类重写后的方法中可以通过 super().xxx() 调用父类中的方法
# 创建父类
class person():
def __init__(self,name,age):
self.name = name
self.age = age
def show(self):
print(f'大家好,我叫{self.name},我今年{self.age}岁了')
# 创建子类
class student(person):
def __init__(self,name,age,stuno):
super().__init__(name,age) # 调用父类的初始化方法
self.stuno = stuno
def show(self):
super().show() #调用父类中的方法
print(f'我来自XXX大学,我的学号是{self.stuno}')
class doctor(person):
def __init__(self,name,age,department):
super().__init__(name,age)
self.department = department
def show(self):
print(f'大家好,我叫:{self.name},我今年:{self.age}岁了,我所在的科室是:{self.department}')
# 创建子类对象
stu = student('wjk',18,1001)
stu.show() # 此时调用的是子类本身的 show 方法
# 输出:大家好,我叫wjk,我今年18岁了
# 我来自XXX大学,我的学号是1001
doc = doctor('yyqx',20,'外科')
doc.show()
# 输出:大家好,我叫:yyqx,我今年:20岁了,我所在的科室是:外科
3、多态:
(1)定义:
指的就是“多种形态”,即便不知道一个变量所引用的对象到底是什么类型,仍然可以通过这个变量调用对象的方法。
(2)特点:
- 在程序运行过程中根据变量所引用对象的数据类型,动态决定调用哪个对象中的方法
- Python语言中的多态,根本不关心对象的数据类型,也不关心类之间是否存在继承关系,只关心对象的行为(方法)。只要不同的类中有同名的方法,即可实现多态
class person():
def eat(self):
print('五谷杂粮')
class cat():
def eat(self):
print('鱼')
class dog():
def eat(self):
print('骨头')
# 三个类中均有同名的类 eat
# 编写函数
def fun(obj):
obj.eat() # 需要通过 obj(对象) 调用 eat 的方法
# 创建三个类的对象
per = person()
c = cat()
d = dog()
# 调用 fun() 函数
fun(per)
# 输出:五谷杂粮
fun(c)
# 输出:鱼
fun(d)
# 输出:骨头
四、object 类
1、特点:
- 所有类直接或间接的父类
- 所有类都拥有 object 类的属性和方法
2、特殊的方法:

class person(object):
def __init__(self,name,age):
self.name = name
self.age = age
def show(self):
print(f'大家好,我叫:{self.name},我今年:{self.age}岁了')
per = person('wjk',18) # 创建对象时,自动调用 __init__ 方法
print(dir(per))
# 输出:['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'show']
print(per) # 自动调用 __str__ 方法
# 输出:<__main__.person object at 0x0000013D8AF5DD30>
# 重写 __str__ 方法
class person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self): # 重写 __str__ 方法
return '这是一个人,具有 name 和 age 两个实例属性'
per = person('wjk', 18)
print(per)
# 输出:这是一个人,具有 name 和 age 两个实例属性
print(per.__str__())
# 输出:这是一个人,具有 name 和 age 两个实例属性
五、特殊方法

a = 2
b = 3
print(a.__add__(b)) # 加法
# 输出:5
print(a.__sub__(b)) # 减法
# 输出:-1
print(a.__mul__(b)) # 乘法
# 输出:6
print(a.__truediv__(b)) # 非整除法
# 输出:0.6666666666666666
print(a.__floordiv__(b)) # 整除
# 输出:0
print(a.__mod__(b)) # 取余
# 输出:2
print(a.__pow__(b)) # 幂运算
# 输出:8
print(f'{a}<{b}吗',a.__lt__(b))
# 输出:2<3吗 True
print(f'{a}<={b}吗',a.__le__(b))
# 输出:2<=3吗 True
print(f'{a}=={b}吗',a.__eq__(b))
# 输出:2==3吗 False
print(f'{a}>{b}吗',a.__gt__(b))
# 输出:2>3吗 False
print(f'{a}>={b}吗',a.__ge__(b))
# 输出:2>=3吗 False
print(f'{a}!={b}吗',a.__ne__(b))
# 输出:2!=3吗 True
六、特殊属性

class A():
pass
class B():
pass
class C(A,B):
def __init__(self,name,age):
self.name = name
self.age = age
a = A()
b = B()
c = C('wjk',18)
print('对象a的属性字典:',a.__dict__)
# 输出:对象a的属性字典: {}
print('对象b的属性字典:',b.__dict__)
# 输出:对象b的属性字典: {}
print('对象c的属性字典:',c.__dict__)
# 输出:对象c的属性字典: {'name': 'wjk', 'age': 18}
print('对象a所属的类:',a.__class__)
# 输出:对象a所属的类: <class '__main__.A'>
print('对象b所属的类:',b.__class__)
# 输出:对象b所属的类: <class '__main__.B'>
print('对象c所属的类:',c.__class__)
# 输出:对象c所属的类: <class '__main__.C'>
print('A类的父类元组:',A.__bases__)
# 输出:A类的父类元组: (<class 'object'>,)
print('B类的父类元组:',B.__bases__)
# 输出:B类的父类元组: (<class 'object'>,)
print('C类的父类元组:',C.__bases__)
# 输出:C类的父类元组: (<class '__main__.A'>, <class '__main__.B'>)
print('A类的父类:',A.__base__)
# 输出:A类的父类: <class 'object'>
print('B类的父类:',B.__base__)
# 输出:B类的父类: <class 'object'>
print('C类的父类:',C.__base__) # 当继承了多个父类时,只显示第一个父类
# 输出:C类的父类: <class '__main__.A'>
print('A类的层次结构:',A.__mro__)
# 输出:A类的层次结构: (<class '__main__.A'>, <class 'object'>)
print('B类的层次结构:',B.__mro__)
# 输出:B类的层次结构: (<class '__main__.B'>, <class 'object'>)
print('C类的层次结构:',C.__mro__) # 直接继承 A、B,间接继承了 object
# 输出:C类的层次结构: (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
print('A类的子类列表:',A.__subclasses__())
# 输出:A类的子类列表: [<class '__main__.C'>]
print('B类的子类列表:',B.__subclasses__())
# 输出:B类的子类列表: [<class '__main__.C'>]
print('C类的子类列表:',C.__subclasses__())
# 输出:C类的子类列表: []
七、类的深拷贝与浅拷贝
1、变量的赋值:
只是形成两个变量,实际上还是指向同一个对象
2、浅拷贝:
拷贝时,对象包含的子对象内容不拷贝,因此,源对象与拷贝对象会引用同一个子对象
3、深拷贝:
使用 copy 模块的 deepcopy 函数,递归拷贝对象中包含的子对象,源对象和拷贝对象所有的子对象也不相同
4、空间图

class CPU():
pass
class Disk():
pass
class Com():
def __init__(self,cpu,disk):
self.cpu = cpu
self.disk = disk
cpu = CPU()
disk = Disk()
# (1)变量的赋值
com = Com(cpu,disk)
com1 = com
print(com,'子对象的内存地址:',com.cpu,com.disk)
# 输出:<__main__.Com object at 0x0000023B45B2E630> 子对象的内存地址: <__main__.CPU object at 0x0000023B45B2E690> <__main__.Disk object at 0x0000023B45B2E600>
print(com1,'子对象的内存地址:',com1.cpu,com1.disk)
# 输出:<__main__.Com object at 0x0000023B45B2E630> 子对象的内存地址: <__main__.CPU object at 0x0000023B45B2E690> <__main__.Disk object at 0x0000023B45B2E600>
# com,com1 指向同一片区域
# (2)浅拷贝
import copy
com2 = copy.copy(com)
print(com,'子对象的内存地址:',com.cpu,com.disk) # com2 为新产生的一个对象,com2的子对象地址不变
# 输出:<__main__.Com object at 0x0000023B45B2E630> 子对象的内存地址: <__main__.CPU object at 0x0000023B45B2E690> <__main__.Disk object at 0x0000023B45B2E600>
print(com2,'子对象的内存地址:',com2.cpu,com2.disk)
# 输出:<__main__.Com object at 0x0000023B45B2E8A0> 子对象的内存地址: <__main__.CPU object at 0x0000023B45B2E690> <__main__.Disk object at 0x0000023B45B2E600>
# (3)深拷贝
import copy
com3 = copy.deepcopy(com)
print(com,'子对象的内存地址:',com.cpu,com.disk) # com3 为新产生的一个对象,com3 的子对象地址也发生改变
# 输出:<__main__.Com object at 0x0000023B45B2E630> 子对象的内存地址: <__main__.CPU object at 0x0000023B45B2E690> <__main__.Disk object at 0x0000023B45B2E600>
print(com3,'子对象的内存地址:',com3.cpu,com3.disk)
# 输出:<__main__.Com object at 0x0000023B45B5D160> 子对象的内存地址: <__main__.CPU object at 0x0000023B45B5D220> <__main__.Disk object at 0x0000023B45B5D280>
微语:
放轻松,任由阳光盈满你,然后闪亮。——玛丽·奥利弗
1587

被折叠的 条评论
为什么被折叠?



