『无为则无心』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支持多层继承 ...
随机推荐
- Kubernetes&Docker集群部署
集群环境搭建 搭建kubernetes的集群环境 环境规划 集群类型 kubernetes集群大体上分为两类:一主多从和多主多从. 一主多从:一台Master节点和多台Node节点,搭建简单,但是有单 ...
- 论文解读《The Emerging Field of Signal Processing on Graphs》
感悟 看完图卷积一代.二代,深感图卷积的强大,刚开始接触图卷积的时候完全不懂为什么要使用拉普拉斯矩阵( $L=D-W$),主要是其背后的物理意义.通过借鉴前辈们的论文.博客.评论逐渐对图卷积有了一定的 ...
- 从数组中找出第K大的数
利用改进的快排方法 public class QuickFindMaxKValue { public static void main(String[] args) { int[] a = {8, 3 ...
- php的CI框架相关数据库操作
在使用之前首先应该配置数据库的参数,详见文件application/config/database.php,里面包含主机名,登陆用户名,登录密码,数据库名,编码信息等. 在配置成功后,可以简单的调用: ...
- MobaXterm中文乱码问题
现在Xshell和SecureCRT都要收费,本着不用盗版的原则,同时需要标签管理session,快捷命令等功能,最后选择了MobaXterm. 但是使用后发现中文会乱码.后按照博客的方法,修改了终端 ...
- collate utf8_bin是什么意思
创建数据库时 collate utf_bin是 以二进制值比较,也就是区分大小写,collate是核对的意思 uft-8_general_ci 一般比较,不区分大小写
- 抽象类 final
抽象类 1.用abstract关键字来修饰一个类时,这个类叫做抽象类,用abstract来修饰一个方法时,这个方法叫抽象方法. 2.含有抽象方法的类必须被声明为抽象类,抽象类必须被继承,抽象方法必须被 ...
- 『无为则无心』Python函数 — 38、Python中的异常
目录 1.异常概念 2.了解异常 3.异常的写法 (1)语法 (2)快速体验 (3)捕获指定异常 (4)异常中的else (5)异常中的finally (6)总结 1.异常概念 定义:程序在运行过程当 ...
- static关键字的一些使用
百度百科定义static关键字 通常情况下,类成员必须通过它的类的对象访问,但是可以创建这样一个成员,它能够被它自己使用,而不必引用特定的实例.在成员的声明前面加上关键字static(静态的)就能创建 ...
- SpringBoot使用异步线程池实现生产环境批量数据推送
前言 SpringBoot使用异步线程池: 1.编写线程池配置类,自定义一个线程池: 2.定义一个异步服务: 3.使用@Async注解指向定义的线程池: 这里以我工作中使用过的一个案例来做描述,我所在 ...