Python基础day08【面向对象(类、对象、属性)、魔方方法(init、str、del、repr)】

 

目录

0、复习

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

2、类内部操作属性

3、魔法方法

3.1、__init__()[掌握]

3.1.1、单参__init__()

3.1.2、多参__init__()

3.2、__str__()[掌握]

3.3、__del__()[理解]

4、案例:烤地瓜

烤地瓜-调料版

5、补充:查看对象的引用计数

6、案例:搬家具

打印家具信息

7、区分__str__()与__repr__()

8、附录:魔方方法(__str__对比__repr__)

8.1、引言

8.2、分析

8.2.1、重写__str__()方法

8.2.2、重写__repr__()方法

8.3、总结

9、总结


0、复习

二进制文件的读写,不需要指定encoding参数。

基础班考试:单选、多选、代码题。

while True: pass 使用场景:在书写代码的时候,不确定循环一共执行多少次,即不知道循环在什么时候结束(具体结束的时间),在运行代码的过程中才能确定,在代码中进行if判断,如果条件不满足,就使用break终止循环。

my_list = list()  # 创建列表类对象
my_dict = dict()  # 创建字典类对象
my_tuple = tuple()  # 创建元组类对象

class 类名(object):
    def 函数名(self):
        pass

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

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

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

# 创建对象
dog1 = Dog()
dog1.play()  # 小狗快乐的拆家中...

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

# 获取对象的属性值:对象.属性名
print(dog1.name)  # 大黄
print(dog1.age)  # 2

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

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

2、类内部操作属性

self 就是一个形参的名字,可以写成其他的形参名,一般不修改这个名字,默认是self。

class Dog(object):
    # self作为类中方法的第一个形参,在通过对象调用方法的时候,不需要手动地传递实参值,
    # 是python解释器自动将调用该方法的对象传递给self, 所以self这个形参代表的是对象
    def play(self):
        print(f'self: {id(self)}')
        print(f'小狗 {self.name} 在快乐地拆家中...')

# 创建对象
dog1 = Dog()
dog1.name = '大黄'
print(f"dog : {id(dog1)}")
dog1.play()
print('-------------------------')
dog2 = Dog()
dog2.name = '小白'
print(f"dog2: {id(dog2)}")
dog2.play()

3、魔法方法

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

如何学习魔法方法:1.魔法方法在什么情况下会自动调用;2.这个魔法方法有什么作用;3.这个魔法方法有哪些注意事项。

3.1、__init__()[掌握]

调用时机:在创建对象之后,会立即调用。类似于Java中的构造方法。
作用:
    1.用来给对象添加属性,给对象属性一个初始值(构造函数);
    2.代码的业务需求,每创建一个对象,都需要执行的代码可以写在`__init__ `中。

注意点:如果`__init__`方法中,有除了 self 之外的形参,那么在创建的对象的时候,需要给额外的形参传递实参值`类名(实参)`

3.1.1、单参__init__()

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


# 创建对象
Dog()  # 我是__init__方法,我被调用了!

dog1 = Dog()  # 我是__init__方法,我被调用了!
print(dog1.name)  # 小狗

dog2 = Dog()  # 我是__init__方法,我被调用了!
print(dog2.name)  # 小狗

3.1.2、多参__init__()

class Dog(object):
    def __init__(self, name):  # self是对象
        print('我是__init__方法,我被调用了!')
        # 对象.属性名 = 属性值
        self.name = name
    def play(self):
        print(f"小狗{self.name}快乐地拆家中...")

# 创建对象 类名(实参值)
dog1 = Dog('大黄')  # 我是__init__方法,我被调用了!
print(dog1.name)  # 大黄
dog1.play()  # 小狗大黄快乐地拆家中...

dog2 = Dog('小白')  # 我是__init__方法,我被调用了!
print(dog2.name)  # 小白
dog2.play()  # 小狗小白快乐地拆家中...

3.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(对象)默认输出对象的引用地址 # <__main__.Dog object at 0x0000020F33D574C0>

str_dog = str(dog)  # 没有定义__str__方法,类型转换,赋值的也是引用地址
print(str_dog)  # <__main__.Dog object at 0x0000020F33D574C0>

3.3、__del__()[理解]

__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、案例:烤地瓜

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

烤地瓜规则:

  1. 地瓜有自己的状态,默认是生的,地瓜可以进行烧烤。
  2. 地瓜有自己烧烤的总时间,由每次烧烤的时间累加得出。
  3. 地瓜烧烤时,需要提供本次烧烤的时间。
  4. 地瓜烧烤时,地瓜状态随着烧烤总时间的变化而改变:[0,3) 生的、[3,6) 半生不熟、[6,8) 熟了、>=8 烤糊了。
  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)  # 地瓜的状态<<生的>>, 烧烤总时间为<0>

potato.cook(4)
print(potato)  # 地瓜的状态<<半生不熟的>>, 烧烤总时间为<4>

potato.cook(3)
print(potato)  # 地瓜的状态<<熟了>>, 烧烤总时间为<7>

烤地瓜-调料版

属性:调料: name_list = []
方法:添加调料 add()

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)  # 地瓜的状态<<生的>>, 烧烤总时间为<0>, 还没有添加调料!

potato.add('油')
potato.cook(4)
potato.add('辣椒面')
print(potato)  # 地瓜的状态<<半生不熟的>>, 烧烤总时间为<4>, 已添加的调料有: 油,辣椒面!

potato.cook(3)
potato.add('孜然')
print(potato)  # 地瓜的状态<<熟了>>, 烧烤总时间为<7>, 已添加的调料有: 油,辣椒面,孜然!

5、补充:查看对象的引用计数

import sys

class Dog(object):
    pass

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

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

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

6、案例:搬家具

搬家具规则:

  1. 家具分不同的类型,并占用不同的面积。
  2. 输出家具信息时,显示家具的类型和家具占用的面积。
  3. 房子有自己的地址和占用的面积。
  4. 房子可以添加家具,如果房子的剩余面积可以容纳家具,则提示家具添加成功;否则提示添加失败。
  5. 输出房子信息时,可以显示房子的地址、占地面积、剩余面积。

类名:家具类 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)

打印家具信息

# 定义家具类 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)

sofa = Furniture('柔软大沙发', 8)
print(sofa)

house.add_furniture(sofa)
print(house)

7、区分__str__()与__repr__()

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

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方法,非常类似,也是必须返回一个字符串!"""
        return f"{self.name}"

# 将三个Dog类的对象添加到列表中
my_list1 = [Dog('大黄', 2), Dog('小白', 4), Dog('小花', 6)]
print(my_list1)  # [大黄, 小白, 小花]

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

8、附录:魔方方法(__str__对比__repr__)

python中的魔法方法:`__str__` 和`__repr__`.pdf

8.1、引言

在学习⾯向对象的时候,我们知道在 python 中有⼀类特殊的⽅法,叫做魔法⽅法,这种⽅法的特点如下:
   1. ⽅法定义的时候以两个下划线开头和两个下划线结尾:如__init__ 、 __str__ 和__repr__;
   2. 这类⽅法⼀般不需要我们⼿动调⽤,在满⾜某个条件的时候会⾃动调⽤,这个满⾜的条件我们可以称为调⽤时机

在Python中有两个魔法⽅法都是⽤来描述对象信息的,__str__和__repr__,那为什么要定义两个这样的⽅法呢,其实是它们设计的⽬的是不⼀样的:
   1.__repr__的⽬标是准确性,或者说,__repr__的结果是让解释器⽤的;
   2.__str__的⽬标是可读性,或者说,__str__的结果是让⼈看的。

8.2、分析

那下边,我们详细地来看⼀下,他们的⽤法:在不重写__str__和__repr__的情况下,打印对象的 输出结果不太友好,是对象的内存地址,即 id的结果。

class Person(object):  # 定义 Person 类
    def __init__(self, name):
        self.name = name

p = Person("isaac")

# 以下为测试输出的结果:
print(p)  # <__main__.Person object at 0x0000016FB4E0E220>
p  # <__main__.Person object at 0x0000016FB4E0E220>
p.__str__()  # '<__main__.Person object at 0x0000016FB4E0E220>'
p.__repr__()  # '<__main__.Person object at 0x0000016FB4E0E220>'

这样的输出结果,并不是我们想要的结果,此时我们重写__str__和__repr__⽅法。

8.2.1、重写__str__()方法

class Person(object):  # 定义 Person 类
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return "__str__ ⽅法 " + self.name

p = Person("isaac")

# 以下为测试输出的结果:
print(p)
str(p)
f"{p}"
p.__str__()
p

此时我们发现在使⽤ print 打印对象、对象的格式化输出以及调⽤str⽅法,调⽤的都是__str__⽅法。但在交互环境下,直接输出对象的时候,没有调⽤__str__⽅法,输出的结果仍然是id的结果。

8.2.2、重写__repr__()方法

class Person(object):  # 定义 Person 类:
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return "__str__ ⽅法 " + self.name
    def __repr__(self):
        return "__repr__ ⽅法 " + self.name

p = Person("isaac")

# 以下为测试输出的结果:
p
p.__repr__()
print(p)

通过简单的对⽐,我们发现,在交互环境下,直接输出对象,调⽤的__repr__⽅法。
另外还需要注意的是,如果将对象放在容器中进⾏输出,调⽤的是__repr__⽅法。

8.3、总结

Python 中的__str__和__repr__⽅法都是⽤来显示的,即描述对象信息的。

1.__str__的⽬标是可读性,或者说,__str__的结果是让⼈看的。主要⽤来打印,即print操作,
2.__repr__的⽬标是准确性,或者说,__repr__的结果是让解释器⽤的。__repr__⽤于交互模式下提示回应,
3.如果没有重写__str__⽅法,但重写了__repr__⽅法时,所有调⽤__str__的时机都会调⽤__repr__⽅法。

9、总结

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

upward337

谢谢老板~

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

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

打赏作者

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

抵扣说明:

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

余额充值