『无为则无心』Python面向对象 — 59、魔法方法
在Python语言中,有些方法名比较特别,在名称的前后各有两个下划线,这样的方法往往具有特殊的意义,我们统称为魔法方法,也叫特殊方法。需要注意的是,我们在创建自定义方法时要避免这样的格式,防止造成不必要的冲突。
Python的魔法方法有很多,我们主要介绍常用的几个魔法方法。以后需要用到其他的魔法方法,按照介绍的这几个调用方式,自己尝试一下就可以了,很简单的。
1、魔法方法__new__()
类的实例化时,调用的第一个方法是__new__方法。
官方定义如下,可以看到__new__被定义成静态方法,第一个必须传入的参数是cls,代表需要实例化的类。
class Demo():
def __new__(cls, *args, **kwargs):
print('调用__new__方法')
def __init__(self):
print('调用__init__方法')
# 结果:调用__new__方法
d = Demo()
从结果看到,只调用了__new__方法,但奇怪的是并没有调用__init__方法,这是为什么呢?
实际上,__new__是负责对当前类进行实例化,并将实例返回,传给__init__方法。__init__方法中的self就是指代__new__传过来的对象,所以再次强调,__init__是实例化后调用的第一个方法。
只有__new__方法返回创建的实例对象,后边的__init__方法才能执行。
class Demo:
def __new__(cls, *args, **kwargs):
print('调用__new__方法')
return object.__new__(cls)
def __init__(self):
print('调用__init__方法')
# 创建对象
d = Demo()
"""
输出结果:
调用__new__方法
调用__init__方法
"""
总结:
__new__()方法是一个魔法方法(同时也是一种静态方法),用于创建一个类的实例,返回一个类的实例对象,用于传入初始化方法__init__()方法中。一般并不需要对其进行声明和重写,若是重写的话,需要注意返回一个有效的类的实例对象,实例通过object父类调用类的实例化方法,用于返回一个对象实例。若是不返回一个类的实例对象,会导致类的__init__()方法不会被调用,当然实例(self为空)也不会被成功创建。
2、魔法方法__init__()
__init__方法是类创建过程中用的比较多的魔法方法,是类对象创建后调用的初始化方法,紧跟者__new__方法后调用,主要用于实例变量的初始化操作。
__init__方法可以理解为一个类的实例的构造器,其方法的特点是不会返回任何对象或者值,若返回则会抛出 TypeError 异常。
(1)体验__init__()
思考:洗衣机的宽度高度是与生俱来的属性,可不可以在生产过程中就赋予这些属性呢?
答:理应如此。
__init__()方法的作用:初始化对象。
示例:
"""
目标: 定义init方法设置初始化属性并访问调用
1. 定义类
init方法: width 和 height
添加实例方法:访问实例属性
2. 创建对象
3. 验证成果
调用实例方法
"""
# 1.定义类
class Washer():
# 定义初始化功能的函数
def __init__(self):
# 添加实例属性
self.width = 500
self.height = 800
def print_info(self):
# 类里面调用实例属性
print(f'洗衣机的宽度是{self.width}, 高度是{self.height}')
# 2. 创建对象
haier1 = Washer()
# 3. 调用实例方法
haier1.print_info()
注意:
__init__()方法,在创建一个对象时默认被调用,不需要手动调用。__init__(self)中的self参数,不需要开发者传递,Python解释器会自动把当前的对象引用传递过去。
(2)带参数的__init__()
思考:一个类可以创建多个对象,如何对不同的对象设置不同的初始化属性呢?
答:传参数。
# 定义类
class Washer():
# 定义带参数的init方法
def __init__(self, width, height):
self.width = width
self.height = height
# 定义实例方法
def print_info(self):
# 调用实例属性
print(f'洗衣机的宽度是{self.width}')
print(f'洗衣机的高度是{self.height}')
haier1 = Washer(10, 20)
haier1.print_info()
# 如果创建对象的时候不传递参数,会报错。
# haier2 = Washer()
haier2 = Washer(100, 200)
haier2.print_info()
图解如下:

3、魔法方法__del__()
当删除对象时,Python解释器也会默认调用__del__()方法。__del__()方法属于析构函数。
__del__()方法不会被主动调用,只有当类的实例对象的引用计数为0时,才会被调用,一般不建议重写__del__()方法。
示例
class Washer():
def __init__(self, width, height):
self.width = width
self.height = height
def __del__(self):
print(f'{self}对象已经被删除')
haier1 = Washer(10, 20)
# <__main__.Washer object at 0x0000026118223278>对象已经被删除
del haier1
即便不手动删除对象,也能调用到
__del__()方法,也就上面示例中,不编写最后del haier1语句,当Python程序执行完成后,也会打印一样的结果,是Python解释器自动调用的。
4、魔法方法__str__()和__repr__()
Python 定义了
__str__()和__repr__()两种方法,__str__()用于显示给用户,而__repr__()用于显示给开发人员。__str__方法默认情况下,它会返回当前对象的类名+object at+内存地址。
如下:<__main__.Students object at 0x0000000002443808>
有时候想让屏幕打印的结果不是对象的内存地址,而是它的形式的字符串,以便更直观地显示对象内容,可以通过在该对象的类中重写
__str__()或__repr__()方法来实现。注意:
__str__()方法和__repr__()方法的返回值只能是字符串!
(1)关于调用两种方法的时机
以下三种场景中,会优先调用对象的__str__()方法。若没有,就调用__repr__()方法。若再没有,则显示其内存地址。
- 使用
print()时。 - 使用
%s和f'{}'拼接对象时。 - 使用
str(x)转换对象x时。
对于下面两种场景:
- 用
%r进行字符串拼接时。 - 用
repr(x)转换对象x时。
则会调用这个对象的__repr__()方法。若没有,则不再看其是否有__str__()方法。若再没有,则显示其内存地址。
(2)示例
__str__方法一般放置的是一些解释说明的文字。
示例:
class Washer():
def __init__(self, width, height):
self.width = width
self.height = height
def __str__(self):
# return repr('这是海尔洗衣机的说明书')
return '这是海尔洗衣机的说明书'
# __repr__方法在实际工作总一般如下使用
__repr__ = __str__
haier1 = Washer(10, 20)
# 结果:这是海尔洗衣机的说明书
print(haier1)
5、魔法方法__call__()
对象后面加括号,触发执行。
提示:
__init__方法的执行是由创建对象触发的,即:对象 = 类名()。- 而对于
__call__方法的执行是由对象后加括号触发的,即:对象()或者类()()。
示例:
class Person(object):
def __init__(self):
print("__init__方法执行了")
def __call__(self, *args, **kwargs):
print("__call__方法中执行了")
# 1.对象 = 类名()
p = Person() # 执行__init__
# 2.对象() 调用__call__方法
p()
# 3.类()() 调用__call__方法
Person()() # 执行__call__
提示:这个方法在工作中的应用自己以后总结,先知道怎么用就好。
6、魔法方法__len__()
__len__()方法在list,dict,set,tuple,str等序列中都存在,而在int和float类中是不存在的。
print('__len__' in dir(int)) # False
print('__len__' in dir(float)) # False
print('__len__' in dir(str)) # True
print('__len__' in dir(list)) # True
print('__len__' in dir(tuple)) # True
print('__len__' in dir(dict)) # True
print('__len__' in dir(set)) # True
如果一个类要表现得像一个list一样,有需求要获取有多少个元素,就得用 len() 函数。
要让 len() 函数工作正常,类中就必须提供一个魔法方法__len__()方法,它的作用是返回元素的个数。
示例:
# 定义一个类,接收同学名的名字
class Students(object):
def __init__(self, *args):
self.names = args
def __len__(self):
return len(self.names)
stu = Students('唐僧', '孙悟空', '猪八戒', '沙和尚')
print(len(stu)) # 4
print(stu.__len__()) # 4
提示:只要在类中正确实现了
__len__()方法,就可以用len()函数。
7、魔法方法__getitem__()、__setitem__()、__delitem__()
在Python中,如果我们想实现创建类似于序列和映射的类,可以通过重写魔法方法__getitem__()方法、__setitem__()方法、__delitem__()方法去模拟。
示例:
class MyDict(object):
def __init__(self):
self.item = {}
# 获取成员
def __getitem__(self, key):
return self.item.get(key)
# 添加成员
def __setitem__(self, key, value):
self.item[key] = value
# 删除成员
def __delitem__(self, key):
del self.item[key]
# 获取人员数量
def __len__(self):
return len(self.item)
if __name__ == "__main__":
# 创建序列对象
myDict = MyDict()
# 添加元素
myDict.__setitem__('师傅', '唐憎')
myDict.__setitem__('大师兄', '孙悟空')
# 查看刚添加的成员
print(myDict.__getitem__('大师兄'))
print(f"现在成员{len(myDict)}人", ) # 2
# 修改成员
myDict.__setitem__('大师兄', '齐天大圣')
print(myDict.__getitem__('大师兄'))
print(f"现在成员{len(myDict)}人", ) # 2
# 删除元素
myDict.__delitem__('师傅')
print(f"现在成员{len(myDict)}人", ) # 1
"""
输出结果:
孙悟空
现在成员2人
齐天大圣
现在成员2人
现在成员1人
"""
参考:
『无为则无心』Python面向对象 — 59、魔法方法的更多相关文章
- 『无为则无心』Python面向对象 — 51、私有成员变量(类中数据的封装)
目录 1.私有成员变量介绍 (1)私有成员变量概念 (2)私有成员变量特点 (3)私有成员变量体验 2.属性私有化工作原理 3.定义成员变量的标识符规范 4.私有成员变量的获取和设置方式 1.私有成员 ...
- 『无为则无心』Python面向对象 — 53、对Python中封装的介绍
目录 1.继承的概念 2.继承的好处 3.继承体验 4.单继承 5.多继承 1.继承的概念 在Python中,如果两个类存在父子级别的继承关系,子类中即便没有任何属性和方法,此时创建一个子类对象,那么 ...
- 『无为则无心』Python面向对象 — 45、面向对象编程
目录 1.面向对象编程的概念 2.面向对象编程和面向过程编程的区别 (1)面向过程编程 (2)面向对象编程 3.举例理解面向对象 4.Python的面向对象编程 5.面向对象的几大核心特性 1.面向对 ...
- 『无为则无心』Python面向对象 — 46、类和对象
目录 1.理解类和对象 2.类 3.对象 4.Python中的对象 5.类和对象的定义 (1)定义类 (2)创建对象 (3)练习 6.拓展:isinstance() 函数 1.理解类和对象 (1)类和 ...
- 『无为则无心』Python面向对象 — 52、私有成员方法(类中行为的封装)
Python对于类的成员没有严格的访问控制限制,这与其他面向对象的编程语言是有所区别的. 关于私有方法其实和私有属性差不多,有如下要点: 1.通常我们约定,两个下划线开头的方法是私有方法. 2.类内部 ...
- 『无为则无心』Python面向对象 — 56、Python多态
目录 1.Python中的多态 (1)多态的定义 (2)多态的好处 (3)多态实现步骤 2.体验多态 1.Python中的多态 (1)多态的定义 多态是一种使用对象的方式,子类重写父类方法,不同的子类 ...
- 『无为则无心』Python面向对象 — 47、Python中的self详解
目录 1.self的作用 2.self的使用注意事项 (1)self代表类的实例,而非类 (2)self不必非写成self,只是一种规范. (3)类中方法的形参中一定要写self,包括内置函数 (4) ...
- 『无为则无心』Python面向对象 — 54、重写和super()函数
目录 1.重写 2.super()函数 方式一 方式二 __mro__内置类属性说明 1.重写 在子类中如果有和父类同名的方法,则通过子类实例去调用该方法时,会调用子类中的该方法而不是父类的方法,这个 ...
- 『无为则无心』Python面向对象 — 55、多层继承和继承中的私有成员
目录 1.Python支持多层继承 (1)多层继承实现 (2)多层继承和多重继承区别 2.继承中的私有成员 (1)继承中父类私有属性和私有方法 (2)获取和修改私有属性值 1.Python支持多层继承 ...
随机推荐
- CTF-sql-宽字节注入
本文章主要涉及sql宽字节注入注入的原理讲解,如有错误,望指出.(附有目录,如需查看请点右下角) 一.首先介绍一下本篇文章所用到的知识点: 常用到的url编码: 空格:%20 单引号:%27 在sql ...
- k8s的应用包管理工具helm的部署和使用
1.概述 我们一般是在k8s里面部署一些简单的应用,比如用deployment,daemonset,statefuleset的方式来部署应用,但是如果要部署一些复杂的应用,那么整个配置的编写.部署的过 ...
- 【代码分享】用redis+lua实现多个集合取交集并过滤,类似于: select key from set2 where key in (select key from set1) and value>=xxx
redis中的zset结构可以看成一个个包含数值的集合,或者认为是一个关系数据库中用列存储方式存储的一列. 需求 假设我有这样一个数据筛选需求,用SQL表示为: select key from set ...
- 学习OAuth 2.0
认识OAuth 2.0 OAuth 2.0 是行业标准的授权协议. OAuth 2.0 专注于客户端开发人员的简单性,同时为 Web 应用程序.桌面应用程序.移动设备提供特定的授权流程. 应用场景 有 ...
- QT之鼠标事件
Widget.h: #ifndef WIDGET_H #define WIDGET_H #include<QWidget> #include<QMouseEvent> clas ...
- Casbin + Gin + Gorm 学习探索
Casbin 是一个强大的,开源的访问控制框架,权限管理机制支持多种访问控制模型: 并且支持多种编程语言: 文档地址:https://casbin.org/docs/zh-CN/overview Gi ...
- How to check in Windows if you are using UEFI
You might be wondering if Windows is using UEFI or the legacy BIOS, it's easy to check. Just fire up ...
- [POI2009]SLO-Elephants
首先可以发现我们可以发现最终状态和初始状态都是一个大小为 \(n\) 的排列,且恰好有 \(n\) 种关系,于是我们对于每个 \(a_i\) 连一条 \(a_i \rightarrow b_i\) 的 ...
- Docker版本Jenkins的使用
一. 什么是Jenkins Jenkins是当前非常流行的一款持续集成工具,可以帮助大家把更新后的代码自动部署到服务器上运行. 二. 为什么用docker版的Jenkins Jenkins主要有三种安 ...
- Redis 分布式锁使用不当,酿成一个重大事故,超卖了100瓶飞天茅台!!!(转)
基于Redis使用分布式锁在当今已经不是什么新鲜事了. 本篇文章主要是基于我们实际项目中因为redis分布式锁造成的事故分析及解决方案.我们项目中的抢购订单采用的是分布式锁来解决的,有一次,运营做了一 ...