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>

微语:

放轻松,任由阳光盈满你,然后闪亮。——玛丽·奥利弗

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值