Python新姿势:用魔法方法玩转对象

发布时间:2023年12月30日


在这里插入图片描述

前言

Python中魔法方法(magic method)其实就是那些被双下划线包围的方法,比如__init____str__ 等等。

这些魔法方法添加了**“魔力”,让我们可以在面向对象编程中用更加简洁的代码来操作对象。
本篇根据面向对象编程的一些场景来介绍常用的
魔法方法**。

1. 对象构建

为了初始化对象,我们常常在对象中定义一个__init__方法,比如:

class Student(object):
    """
    学生信息
    """
    def __init__(self, name):
        print("[__init__] Student 初始化...")
        self.name = name

# 创建对象
stu = Student("小明")

# 运行结果
[__init__] Student 初始化...

当我们创建对象时,会自动调用魔法方法__init__去初始化属性。

__init__对应的是__del__,它会在对象失效的调用,
我们可以在__del__方法中清理对象Student占用的资源。

class Student(object):
    def __init__(self, name):
        print("[__init__] 初始化 Student...")
        self.name = name

    def __del__(self):
        print("[__del__] 清理 Student...")

def foo():
    print("进入 foo 函数...")
    stu = Student("小明")
    print("离开 foo 函数...")

foo()

# 运行结果
进入 foo 函数...
[__init__] 初始化 Student...
离开 foo 函数...
[__del__] 清理 Student...

进入 foo 函数后创建对象,离开后,自动清理对象。

2. 对象属性访问

python的对象是可以直接访问属性的,但是访问不存在的属性时,会抛出异常。

class Student(object):
    def __init__(self, name):
        print("[__init__] 初始化 Student...")
        self.name = name

stu = Student("小明")
print(stu.name)
print(stu.fullname)

# 运行结果
[__init__] 初始化 Student...
小明

AttributeError: 'Student' object has no attribute 'fullname'

如果不想抛出异常,从而影响程序的其他部分的运行,
这里可以用魔法方法 __getattr__来控制访问了不存在的属性时的行为。
比如,下面的示例中,访问不存在的属性时,返回False,不抛出异常。

class Student(object):
    def __init__(self, name):
        print("[__init__] 初始化 Student...")
        self.name = name

    def __getattr__(self, attr):
        print("[__getattr__] 访问了 Student 不存在的属性 {}".format(attr))
        return False

stu = Student("小明")
print(stu.name)
print(stu.fullname)

# 运行结果
[__init__] 初始化 Student...
小明
[__getattr__] 访问了 Student 不存在的属性 fullname
False

__getattr__相对的另一个魔法方法__setattr__,与__getattr__不同的是,它既可以设置不存在的属性,也可以设置已存在的属性。

class Student(object):
    def __init__(self, name):
        print("[__init__] 初始化 Student...")
        self.name = name

    def __setattr__(self, attr, value):
        print("[__setattr__] 设置 Student 属性 {}={}".format(attr, value))
        self.__dict__[attr] = value

stu = Student("小明")
print(stu.name)

# 已存在的属性
stu.name = "小华"
print(stu.name)

# 不存在的属性
stu.fullname = "小红"
print(stu.fullname)

# 运行结果
[__init__] 初始化 Student...
[__setattr__] 设置 Student 属性 name=小明
小明
[__setattr__] 设置 Student 属性 name=小华
小华
[__setattr__] 设置 Student 属性 fullname=小红
小红

每次对属性赋值,都会触发__setattr__方法,
即使在__init__中给self.name赋值,也触发了__setattr__方法,所以一共被触发了三次。

3. 对象比较

在使用基本类型(比如数值类型,字符串类型等)的时候,比较其大小非常简单。

print(1 < 2)
print(1 > 2)
print("abc" < "edf")

# 运行结果
True
False
True

而对象直接互相比较大小则没有这么简单了,还是用上面的Student类,这次我们加上各门功课的分数。

class Student(object):
    def __init__(self, name, chinese, english, mathematics):
        self.name = name
        self.chinese = chinese
        self.english = english
        self.mathematics = mathematics

stu1 = Student("小明", 80, 90, 75)
stu2 = Student("小红", 85, 70, 95)

# 比较两个学生对象
print(stu1 > stu2)

# 运行结果
TypeError: '>' not supported between instances of 'Student' and 'Student'

不出所料,抛出了异常。
其实我们比较两个学生对象,是想要比较两个学生的总分谁高谁低。

这时,我们就可以实现用于比较的魔法方法,比如 __eq____lt____gt__等等。

class Student(object):
    def __init__(self, name, chinese, english, mathematics):
        self.name = name
        self.chinese = chinese
        self.english = english
        self.mathematics = mathematics

    def total(self):
        return self.chinese + self.english + self.mathematics

    def __eq__(self, other: Student):
        return self.total() == other.total()

    def __lt__(self, other: Student):
        return self.total() < other.total()

    def __gt__(self, other: Student):
        return self.total() > other.total()

stu1 = Student("小明", 80, 90, 75)
stu2 = Student("小红", 85, 70, 95)

print(stu1 > stu2)
print(stu1 == stu2)
print(stu1 < stu2)

# 运行结果
False
False
True

实现相应的魔法方法,就能使用相应的比较运算符来直接比较对象了。
PS. 上面的例子中只实现了大于__gt__),等于__eq__),小于__lt__)的比较,
还有其他魔法方法,比如:大于等于__ge__),不等于__ne__),小于等于__le__)等等。
实现的方式和上面例子中类似。

4. 对象输出

对于一个基本类型的变量,显示其内容非常简单,而对于一个对象,显示的内容可能就不是我们所期望的了。

class Student(object):
    def __init__(self, name, chinese, english, mathematics):
        self.name = name
        self.chinese = chinese
        self.english = english
        self.mathematics = mathematics

s = "hello world"
print(s)

stu = Student("小明", 80, 90, 75)
print(stu)

# 运行结果
hello world
<__main__.Student object at 0x00000164F72FD6D0>

字符串内容可以直接显示出来,而对象stu只是显示了它的内存地址,对我们了解其中的内容毫无帮助。

此时,就可以拿出我们的魔法方法__str__,来定制对象在print时显示的内容。

class Student(object):
    def __init__(self, name, chinese, english, mathematics):
        self.name = name
        self.chinese = chinese
        self.english = english
        self.mathematics = mathematics

    def __str__(self):
        return """姓名: {}
成绩:
1. 语文: {} 分
2. 数学: {} 分
3. 英语: {} 分""".format(
            self.name,
            self.chinese,
            self.mathematics,
            self.english,
        )

stu = Student("小明", 80, 90, 75)
print(stu)

# 运行结果
姓名: 小明
成绩:
1. 语文: 80 分
2. 数学: 75 分
3. 英语: 90 分

通过魔法方法__str__,可以让对象按照我们希望的形式显示出来。

5. 对象运算

对象除了可以像普通变量一样比较输出,是不是也可以像变量一样进行算术运算呢?
比如:

class Student(object):
    def __init__(self, name, scores):
        self.name = name
        self.scores = scores

    def __str__(self):
        return """姓名: {}, 各科成绩: {}""".format(
            self.name,
            self.scores,
        )

stu1 = Student("小明", [80, 90, 75])
stu2 = Student("小红", [85, 70, 95])
print(stu1)
print(stu2)
print(stu1 + stu2)

# 运行结果
姓名: 小明, 各科成绩: [80, 90, 75]
姓名: 小红, 各科成绩: [85, 70, 95]

TypeError: unsupported operand type(s) for +: 'Student' and 'Student'

果然,直接进行算术运算是不行的。

还是得借助魔法方法,下面在类中实现加法减法魔法方法

class Student(object):
    def __init__(self, name, scores):
        self.name = name
        self.scores = scores

    def __str__(self):
        return """姓名: {}, 各科成绩: {}""".format(
            self.name,
            self.scores,
        )

    def __add__(self, other: Student):
        name = "{}, {} 成绩合计".format(self.name, other.name)
        scores = [self.scores[i] + other.scores[i] for i in range(len(self.scores))]
        return Student(name, scores)

    def __sub__(self, other: Student):
        name = "{}, {} 成绩之差".format(self.name, other.name)
        scores = [self.scores[i] - other.scores[i] for i in range(len(self.scores))]
        return Student(name, scores)


stu1 = Student("小明", [80, 90, 75])
stu2 = Student("小红", [85, 70, 95])
print(stu1)
print(stu2)
print(stu1 + stu2)
print(stu1 - stu2)

# 运行结果
姓名: 小明, 各科成绩: [80, 90, 75]
姓名: 小红, 各科成绩: [85, 70, 95]
姓名: 小明, 小红 成绩合计, 各科成绩: [165, 160, 170]
姓名: 小明, 小红 成绩之差, 各科成绩: [-5, 20, -20]

其他的算术运算(比如,乘法,除法和求模等等)也有相应的魔法方法,仿照上面的示例实现即可。

6. 总结

Python魔法方法很多,本文只是列举了其中很少的一部分,
github上有一个示例python文件,列举了很多魔法方法,供参考:magicmethods.py


Python技术资源分享

小编是一名Python开发工程师,自己整理了一套 【最新的Python系统学习教程】,包括从基础的python脚本到web开发、爬虫、数据分析、数据可视化、机器学习等。

保存图片微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

如果你是准备学习Python或者正在学习,下面这些你应该能用得上:

1、Python所有方向的学习路线

Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。

在这里插入图片描述

2、学习软件

工欲善其事必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。

在这里插入图片描述

3、入门学习视频

我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。

img

4、实战案例

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

img

5、清华编程大佬出品《漫画看学Python》

用通俗易懂的漫画,来教你学习Python,让你更容易记住,并且不会枯燥乏味。

在这里插入图片描述

6、Python副业兼职与全职路线

在这里插入图片描述
这份完整版的Python全套学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

👉CSDN大礼包:《Python入门资料&实战源码&安装工具】免费领取安全链接,放心点击

文章来源:https://blog.csdn.net/2301_80240808/article/details/135304917
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。