面向对象(上)

一、定义

概念介绍

==面向对象和面向过程,是两种编程思想.== 编程思想是指对待同一个问题,解决问题的套路方式.

  • 面向过程: 注重的过程,实现的细节.亲力亲为.

    • 函数

  • 面向对象: 关注的是结果, 偷懒.

    • 封装, 继承 和多态(三大技术)

类和对象

类和对象,是面向对象中非常重要的两个概念

    • 类是泛指的,指代多个, 是由对的抽象来的

    • 在代码中, 是通过关键字class定义的

  • 对象

    • 对象是特指的,指代一个,是具体存在的

    • 在代码中, 对象是由类创建的

类的组成

  • 类名

  • 属性: 特性, 变量

    • 一个类的属性有很多,值关注需要使用的,没有用到的不用管

  • 方法: 行为, 函数

类的抽象

  • 名词提炼法

  • 所见即所得

1、类的定义

# 在python中,定义类使用关键字 class , 语法如下
"""
# object 是所有的类基类,即最初始的类
# 类名: 遵循大驼峰的命名规范
class 类名(object):
    类中的代码
"""


新式类: 直接或者间接继承object的类, 在python3中,所有的类默认继承object类,即python3中所有的类都是新式类
旧式类(经典类): 已经过时,不推荐使用

随后的代码,使用狗类进行演示说明
类名: Dog(狗类)
属性:
    姓名 name
    年龄 age
    ....
方法:
    吃  eat()
    玩  play()
    .....



# 定义方式一, 推荐
class Dog(object):
    pass


# 定义方式二
class Dog1():   # 括号中的内容可以不写
    pass


# 定义方式三
class Dog2:   # 括号也可以不写
    pass

2、创建对象

# 定义类
class Dog(object):
    # 在类中定义的函数,称为方法, 函数的所有知识都可以使用
    def play(self):  # 暂时不管self 


# 创建对象 变量 = 类名()
dog = Dog()  # 创建一个对象,dog

dog1 = Dog()  # 创建一个对象 dog1

# 可以使用对象调用类中的方法.  对象.方法名()

# 定义类
class Dog(object):
    # 在类中定义的函数,称为方法, 函数的所有知识都可以使用
    def play(self):  # 暂时不管self ,
        print('小狗快乐的拆家中......')


# 创建对象 变量 = 类名()
dog = Dog()  # 创建一个对象,dog
print(id(dog))

dog1 = Dog()  # 创建一个对象 dog1
print(id(dog1))

# 可以使用对象调用类中的方法.  对象.方法名()
dog.play()
dog1.play()

3、类外部添加和获取对象属性

每个对象都会保存自己的属性值,不同对象的属性值之间是没有关联的 

给对象添加属性  对象.属性名  = 属性值

class Dog(object):
    def play(self):
        print('小狗快乐的拆家中.......')


# 创建对象
dog = Dog()
dog.play()


# 给对象添加属性  对象.属性名  = 属性值
dog.name = '大黄'  # 给dog对象添加name属性  属性值是大黄
dog.age = 2  # 给狗对象添加name属性,属性值是2

# 获取对象的属性值   对象.属性名
print(dog .name)
print(dog .age)

# 修改属性值 和添加一样,存在就是修改,不存在,就是添加
dog.age = 3  # age 属性已经存在,所以是修改属性值
print(dog.age)

dog1 = Dog()  # 新创建一个对象 dog1
dog1.name = '小白'
print(dog1.name)

4、类内部通过self操作属性 

self作为类中方法的第一个形参(只是个名字,可以修改,习惯上默认是self),在通过对象调用方法的时候,不需要手动的传递实参值,是Python解释器自动的将调用该方法的对象传递给self,所以self这个形参代表的是对象

class Dog(object):
    def play(self):
        print(f'self:{id(self)}')  # dog : 1689930204928
        print(f'小狗:{id(self)}在快乐的拆家中....')  # dog : 1689930204928


# 创建对象
dog = Dog()
dog.name = '大黄'
print(f"dog : {id(dog)}")
dog.play()
print('-' * 30)
dog1 = Dog()
dog1.name = '小白'
print(f"dog1: {id(dog1)}")
dog1.play()
'''
dog : 2206774822656
self:2206774822656
小狗:2206774822656在快乐的拆家中....
------------------------------
dog1: 2206779345936
self:2206779345936
小狗:2206779345936在快乐的拆家中....'''

二、魔法方法

 在 python 的类中,有一类方法,这类方法以 `两个下划线开头` 和`两个下划线结尾`, 并且在`满足某个特定条件的情况下,会自动调用`. 这类方法,称为魔法方法

1、__init__()方法

调用时机: 在创建对象之后,会立即调用.
作用: 
    1. 用来给对象添加属性,给对象属性一个初始值(构造函数)
    2. 代码的业务需求,每创建一个对象,都需要执行的代码可以写在 `__init__ `中
注意点: 如果 `__init__` 方法中,有出了 self 之外的形参,那么在创建的对象的时候,需要给额外的形参传递实参值 `类名(实参)`

 对象.属性名 = 属性值

未带参数的:

class Dog(object):
    def __init__(self):  # self 是对象
        print('我是__init__方法,我被调用了')
        # 对象.属性名 = 属性值
        self.name = '小狗'


# 创建对象
# Dog()
dog = Dog()
print(dog.name)
dog1 = Dog()
print(dog1.name)

带参数的: 

class Dog(object):
    def __init__(self, name):  # self 是对象
        print('我是__init__方法,我被调用了')
        # 对象.属性名 = 属性值
        self.name = name

    def play(self):
        print(f"小狗{self.name}快乐的拆家中...")


# 创建对象 类名(实参值)
dog = Dog('大黄')
print(dog.name)
dog.play()
dog1 = Dog('小白')
print(dog1.name)
dog1.play()
'''我是__init__方法,我被调用了
大黄
小狗大黄快乐的拆家中...
我是__init__方法,我被调用了
小白
小狗小白快乐的拆家中...'''
print(dog)  # <__main__.Dog object at 0x0000020C4D448700>
#           原因是没有添加str方法,返回的是一个引用地址,不是具体消息

2、__str__()方法 

调用时机:
    1. `print(对象)`, 会自动调用 `__str__` 方法, 打印输出的结果是 `__str__` 方法的返回值
    2. `str(对象)`  类型转换,将自定义对象转换为字符串的时候, 会自动调用
应用:
    1. 打印对象的时候,输出一些属性信息
    2. 需要将对象转换为字符串类型的时候
注意点:
    `方法必须返回一个字符串`,只有 self 一个参数

class Dog(object):
    def __init__(self,name,age):
        # 添加属性
        self.name = name
        self.age = age

    def __str__(self):
        print('我是__str__, 我被调用了...')
        # 必须返回一个字符串
        return f"小狗的名字是{self.name}, 年龄是{self.age}"


# 创建对象
dog = Dog('大黄', 2)
print(dog)  # 没有定义 __str__ 方法,print(对象) 默认输出对象的引用地址

str_dog = str(dog)  # 没有定义 __str__ 方法, 类型转换,赋值的也是引用地址
# print(str_dog)

3、__del__()方法

析构函数
调用时机:
    对象在内存中被销毁删除的时候(引用计数为 0)会自动调用 __del__ 方法
    1. 程序代码运行结束,在程序运行过程中,创建的所有对象和变量都会被删除销毁
    2. 使用 `del 变量` , 将这个对象的引用计数变为 0.会自动调用 __del__ 方法
应用场景:
    对象被删除销毁的时候,要书写的代码可以写在 `__del__`中.一般很少使用

引用计数: 是 python 内存管理的一种机制, 是指一块内存,有多少个变量在引用,
1. 当一个变量,引用一块内存的时候,引用计数加 1
2. 当删除一个变量,或者这个变量不再引用这块内存.引用计数减 1
3. 当内存的引用计数变为 0 的时候,这块内存被删除,内存中的数据被销毁

my_list = [1, 2]  # 1
my_list1 = my_list # 2
del my_list  # 1
del my_list1 # 0 
 

class Dog(object):
    def __init__(self, name, age):
        # 添加属性
        self.name = name
        self.age = age

    def __str__(self):
        # 必须返回一个字符串
        return f"小狗的名字是{self.name}, 年龄是{self.age}"

    def __del__(self):
        print(f'我是__del__ 方法,我被调用了, {self.name}被销毁了.......')


# 创建一个对象
# dog = Dog('大黄', 2)
# dog1 = Dog('小白', 1)

dog = Dog('小花', 3)  # 小花 引用计数为1
dog2 = dog   # 小花 引用计数2
print('第一次删除之前')
del dog  # dog 变量不能使用, 小花对象引用计数 1
print('第一次删除之后')
print('第二次删除之前')
del dog2  # dog2变量不能使用, 小花对象的引用计数为 0, 会立即__del__ 方法
print('第二次删除之后')

 4、查看对象的引用计数

需要导入sys包

import sys


class Dog(object):
    pass


dog = Dog()  # 1
print(sys.getrefcount(dog))  # 显示的时候,会比实际的多一个,
dog1 = dog  # 2
print(sys.getrefcount(dog))  # 显示的时候,会比实际的多一个,
del dog  # 1
print(sys.getrefcount(dog1))  # 显示的时候,会比实际的多一个,

5、案例:烤地瓜

类名: 地瓜类 Potato
属性:
    状态 status='生的'
    烧烤总时间 total_time = 0
方法:
    def cook(self, 烧烤时间):
        计算烧烤的总时间
        修改地瓜的状态的
        pass
    输出信息  __str__()
    定义属性  __init__()  

基础案例:

class Potato(object):
    def __init__(self):
        self.status = '生的'
        self.total_time = 0

    def cook(self, time):
        # 计算总时间
        self.total_time += time
        # 修改地瓜的状态
        if self.total_time < 3:
            self.status = '生的'
        elif self.total_time < 6:
            self.status = '半生不熟的'
        elif self.total_time < 8:
            self.status = '熟了'
        else:
            self.status = '烤糊了'

    def __str__(self):
        return f"地瓜的状态<<{self.status}>>, 烧烤总时间为<{self.total_time}>"


# 创建对象
potato = Potato()
print(potato)
potato.cook(4)
print(potato)
potato.cook(3)
print(potato)

'''
地瓜的状态<<生的>>, 烧烤总时间为<0>
地瓜的状态<<半生不熟的>>, 烧烤总时间为<4>
地瓜的状态<<熟了>>, 烧烤总时间为<7>
'''

进阶版:(添加调料)

在添加调料中,因为调料是个列表,在输出中比较违和,所以我们可以采用join方法,去掉中括号和引号

class Potato(object):
    def __init__(self):
        self.status = '生的'
        self.total_time = 0
        self.name_list = []  # 保存调料的

    def cook(self, time):
        # 计算总时间
        self.total_time += time
        # 修改地瓜的状态
        if self.total_time < 3:
            self.status = '生的'
        elif self.total_time < 6:
            self.status = '半生不熟的'
        elif self.total_time < 8:
            self.status = '熟了'
        else:
            self.status = '烤糊了'

    def __str__(self):
        # buf_list = str(self.name_list)  # str([])  ===> '[]'
        # buf_list = buf_list.replace('[', '')
        # 字符串.join(列表), 将字符串添加到列表中的每个元素之间,组成新的字符串
        buf = ','.join(self.name_list)
        if self.name_list:
            return f"地瓜的状态<<{self.status}>>, 烧烤总时间为<{self.total_time}>, 调料有: {buf}"
        else:
            return f"地瓜的状态<<{self.status}>>, 烧烤总时间为<{self.total_time}>,还没有添加调料"

    def add(self, name):
        self.name_list.append(name)


# 创建对象
potato = Potato()
print(potato)
potato.add('油')
potato.cook(4)
potato.add('辣椒面')
print(potato)
potato.cook(3)
potato.add('孜然')
print(potato)

'''
地瓜的状态<<生的>>, 烧烤总时间为<0>,还没有添加调料
地瓜的状态<<半生不熟的>>, 烧烤总时间为<4>, 调料有: 油,辣椒面
地瓜的状态<<熟了>>, 烧烤总时间为<7>, 调料有: 油,辣椒面,孜然
'''

6、案例:搬家具

封装的小套路:
1. 根据文字的描述信息,确定对象,对象有什么,就是属性
2. 根据文字的描述信息, 对象能干什么,就是方法
3. 根据文字的描述信息, 确定方法怎么书写

类名: 家具类 Furniture 
属性:
    类型 name 
    面积 area
方法:
    输出家具信息  __str__ 
    定义属性  __init__ 
--------
类名: 房子类 House
属性:
    地址 address
    面积 h_area
    家具列表  furniture_list = []
方法:
    添加家具  add_furniture()
    输出房子信息 __str__ 
    定义属性  __init__  

# 定义家具类 Furniture 类
class Furniture(object):
    def __init__(self, name, area):
        # 类型
        self.name = name
        # 面积
        self.area = area

    def __str__(self):
        return f'家具的类型<{self.name}>, 占地面积<{self.area}>平'


# 定义房子类
class House(object):
    def __init__(self, address, area):
        self.address = address
        self.h_area = area
        self.furniture_list = []
        self.free_area = area  # 房子的剩余面积

    def add_furniture(self, obj_furniture):
        """ 添加家具 obj_furniture:  家具类的对象"""
        if self.free_area > obj_furniture.area:
            self.furniture_list.append(obj_furniture)
            # 修改剩余面积
            self.free_area -= obj_furniture.area
            print(f'家具<{obj_furniture.name}>添加成功')
        else:
            print('添加失败,换个大房子吧')

    def __str__(self):
        return f"房子的地址为<{self.address}>, 占地面积为<{self.h_area}>, 剩余面积为{self.free_area}"


# 创建家具对象
bed = Furniture('豪华双人床', 15)
print(bed)

# 创建一个房子类对象
house = House('意大利农场', 100)
print(house)
house.add_furniture(bed)
print(house)

'''
家具的类型<豪华双人床>, 占地面积<15>平
房子的地址为<意大利农场>, 占地面积为<100>, 剩余面积为100
家具<豪华双人床>添加成功
房子的地址为<意大利农场>, 占地面积为<100>, 剩余面积为85

'''

 添加家具信息(提升难度)

重点关注:

def __str__(self):
    # 自定义家具类,将该类的对象添加到列表中(容器), 直接打印列表,显示的是 自定义对象的引用地址!!!
    # [家具对象, 家具对象, ... ]  ---> [家具类型, 家具类型, .....]
    if self.furniture_list:
        buf_list = [obj.name for obj in self.furniture_list]
        return f"房子的地址为<{self.address}>, 占地面积为<{self.h_area}>, 剩余面积为{self.free_area}," \
               f"家具有<{','.join(buf_list)}>"
    else:
        return f"房子的地址为<{self.address}>, 占地面积为<{self.h_area}>, 剩余面积为{self.free_area}," \
               f"还没有购买家具"
# 定义家具类 Furniture 类
class Furniture(object):
    def __init__(self, name, area):
        # 类型
        self.name = name
        # 面积
        self.area = area

    def __str__(self):
        return f'家具的类型<{self.name}>, 占地面积<{self.area}>平'


# 定义房子类
class House(object):
    def __init__(self, address, area):
        self.address = address
        self.h_area = area
        self.furniture_list = []
        self.free_area = area  # 房子的剩余面积

    def add_furniture(self, obj_furniture):
        """ 添加家具 obj_furniture:  家具类的对象"""
        if self.free_area > obj_furniture.area:
            self.furniture_list.append(obj_furniture)
            # 修改剩余面积
            self.free_area -= obj_furniture.area
            print(f'家具<{obj_furniture.name}>添加成功')
        else:
            print('添加失败,换个大房子吧')

    def __str__(self):
        # 自定义家具类,将该类的对象添加到列表中(容器), 直接打印列表,显示的是 自定义对象的引用地址!!!
        # [家具对象, 家具对象, ... ]  ---> [家具类型, 家具类型, .....]
        if self.furniture_list:
            buf_list = [obj.name for obj in self.furniture_list]
            return f"房子的地址为<{self.address}>, 占地面积为<{self.h_area}>, 剩余面积为{self.free_area}," \
                   f"家具有<{','.join(buf_list)}>"
        else:
            return f"房子的地址为<{self.address}>, 占地面积为<{self.h_area}>, 剩余面积为{self.free_area}," \
                   f"还没有购买家具"


# 创建家具对象
bed = Furniture('豪华双人床', 15)
print(bed)

# 创建一个房子类对象
house = House('意大利农场', 100)
print(house)
house.add_furniture(bed)
print(house)

'''
家具的类型<豪华双人床>, 占地面积<15>平
房子的地址为<意大利农场>, 占地面积为<100>, 剩余面积为100,还没有购买家具
家具<豪华双人床>添加成功
房子的地址为<意大利农场>, 占地面积为<100>, 剩余面积为85,家具有<豪华双人床>'''

7、__repr__()方法

 repr方法和 str方法非常相似,这个是直接打印列表的(容器的),大多数情况下还是使用str方法
        :return:返回的也是字符串

my_list = ['hello', 'python', 'cpp']   # 列表中存储了三个字符串对象

print(my_list)


class Dog(object):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f'{self.name}, {self.age}'

    def __repr__(self):
        """
        repr方法和 str方法非常相似,这个是直接打印列表的(容器的),大多数情况下还是使用str方法
        :return:返回的也是字符串
        """
        return f'{self.name}'

# 将三个Dog类的对象添加到列表中
my_list1 = [Dog('大黄', 2), Dog('小白', 4), Dog('小花', 6)]
print(my_list1)  # [<__main__.Dog object at 0x00000250193279D0>, <__main__.Dog object at 0x0000025019357CA0>, <__main__.Dog object at 0x0000025019364130>]
#                在没有加repr方法前,显示的是这个,引用地址

dog = Dog('大黄', 2)
print(dog)   # __str__

'''
['hello', 'python', 'cpp']
[大黄, 小白, 小花]
大黄, 2
'''

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

名之以父

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值