Python——魔法方法

魔法方法

基本介绍

魔法方法的官方名称为special method,指的是Python自带的,具有强大功能的,在一些特定情况下会被自动调用的方法,方法名一般以两个下划线开始,并且以两个下划线结束,Python中大致有100多个魔法方法

具体方法讲解

1.call()

自动被调用的场景
具有该方法的类对应的实例对象被调用时,自动调用该方法

作用
使得一个自定义的实例对象可以像函数那样被调用

案例

class C:
    def __call__(self, data):       #定义魔法方法__call__
        print('__call__方法被调用')
        print(data)

ins = C()
ins(10)     #通过调用实例对象,自动调用__call__方法

#输出结果:
"""
__call__方法被调用
10
"""

2.enter()

自动被调用的场景
与上下文管理语句(with语句)联合使用,在上下文管理器创建以后,自动调用上下文管理器的该方法

作用
在上下文管理语句中实现资源的访问

案例

class C:
    def __init__(self):
        self.data = 10

    def __enter__(self):		#定义魔法方法__enter__
        print('__enter__方法被调用')
        return self				#将实例对象本身返回

    def __exit__(self, *args):	#定义魔法方法__exit__
        print('__exit__方法被调用', *args)

with C() as ins:		#通过上下文管理语句,在特定时刻自动调用__enter__和__exit__
    print(ins.data)

#输出结果:
"""
__enter__方法被调用
10
__exit__方法被调用 None None None
"""

2.exit()

自动被调用的场景
与上下文管理语句(with语句)联合使用,在上下文关联语句代码块执行完毕后,自动调用上下文管理器的该方法

作用
在上下文管理语句中实现资源的关闭,并且可以获取异常的相关信息

值得注意的是如果__exit__配合上下文管理语句使用,必须要设置三个形参(除了self以外的三个形参),用来接收自动传入的三个异常的相关信息

__exit__ 方法中有三个形参,从左往右依次为exc_type(接收异常类型),exc_val(接收异常值),exc_tb(接收异常回溯追踪信息),即这三个形参用来接收处理异常,如果代码在运行时发生异常,异常会被保存到这里

案例

class C:
    def __init__(self):
        self.data = 10

    def __enter__(self):		#定义魔法方法__enter__
        print('__enter__方法被调用')
        return self				#将实例对象本身返回

    def __exit__(self, *args):	#定义魔法方法__exit__
        print('__exit__方法被调用', *args)

with C() as ins:		#通过上下文管理语句,在特定时刻自动调用__enter__和__exit__
    print(ins.data)

#输出结果:
"""
__enter__方法被调用
10
__exit__方法被调用 None None None
"""

3.new()

自动被调用的场景
在要创建一个实例对象的时候,被自动调用(调用时会自动传入类对象以及在代码"ins = C(指定的实参)"中指定的实参)

作用
创建一个实例对象

值得一提的是__new__方法的返回值就是最后得到的ins,即两者的引用相同

案例

class C:
    def __new__(cls):       #重写魔法方法__new__
        print('__new__方法被调用')
        print(cls)
        ins = super().__new__(cls)      #调用父类的__new__方法创建实例对象
        print(ins)
        return ins          #返回实例对象

ins = C()       #通过实例化对象的操作,自动调用__new__方法
print(ins)

#输出结果:
"""
__new__方法被调用
<class '__main__.C'>
<__main__.C object at 0x0000020B2EA2ADC0>
<__main__.C object at 0x0000020B2EA2ADC0>
"""
class C:
    def __new__(cls):       #重写魔法方法__new__
        print('__new__方法被调用')
        return 123

ins = C()
print(ins)

#输出结果:
"""
__new__方法被调用
123
"""
class C:
    def __new__(cls, data):         #会自动传入类对象和123
        print('__new__方法被调用')
        print(data)
        return super().__new__(cls)

ins = C(123)

#输出结果:
"""
__new__方法被调用
123
"""

4.init()

自动被调用的场景
_new__方法调用结束后,在_new__方法返回的是本类对象对应的实例对象的时候,会自动调用__init__方法(调用时会自动传入__new__方法返回的实例对象以及在代码"ins = C(指定的实参)"中指定的实参)

作用
对通过__new__方法创建的实例对象进行初始化(比如为该实例对象创建实例属性)

案例

class C:
    def __new__(cls):       
        print('__new__方法被调用')
        ins = super().__new__(cls)
        print(ins)
        return ins

    def __init__(self):     #重写魔法方法__init__
        print('__init__方法被调用')
        print(self)

ins = C()       #通过实例化对象的操作,自动调用__init__方法
print(ins)

#输出结果:
"""
__new__方法被调用
<__main__.C object at 0x00000185ABF3ADC0>
__init__方法被调用
<__main__.C object at 0x00000185ABF3ADC0>
<__main__.C object at 0x00000185ABF3ADC0>
"""
class C:
    def __new__(cls):       
        print('__new__方法被调用')
        return 123		#当__new__的返回值不是类对象C对应的实例对象的时候,不会自动调用__init__方法

    def __init__(self):		
        print('__init__方法被调用')

ins = C()
print(ins)

#输出结果:
"""
__new__方法被调用
123
"""
class C:
    def __new__(cls, data):       
        print('__new__方法被调用')
        print('__new__方法的', data)
        ins = super().__new__(cls)
        return ins

    def __init__(self, data):       #会自动传入__new__返回的实例对象和123
        print('__init__方法被调用')
        print('__init__方法的', data)
        self.data = data			#为实例对象创建一个实例属性


ins = C(123)
print(ins.data)

#输出结果:
"""
__new__方法被调用
__new__方法的 123
__init__方法被调用
__init__方法的 123
123
"""

__new__方法与__init__方法

一整个创建实例对象的过程

  • 先自动调用__new__方法
    • 如果__new__方法的返回值为一个该类对象对应的实例对象,则自动调用__init__方法,对这个实例对象进行初始化
    • 如果__new__方法的返回值不是一个该类对象对应的实例对象,则不会自动调用__init__方法,直接进行赋值操作(这种情况只会出现在把__new__方法重写的情况下,一般不会这么干)
  • 最后才会执行语句"实例对象 = 类()"中的赋值操作

值得一提的是创建实例对象的过程中,在代码"ins = C(指定的实参)"中指定的实参会自动传入__new__方法与__init__方法中(如果方法被自动调用的话)

5.iter()

自动被调用的场景
对该对象调用iter函数的时候,会被自动调用

作用
在类中定义了__iter__方法,这个类创建出来的对象就是一个可迭代对象,该方法必须返回一个迭代器,具体的案例请移步Python——迭代器

案例

class C:
    def __iter__(self):         #定义魔法方法__iter__
        print('__iter__方法被调用')
        return iter([1, 2])     #__iter__必须要返回一个迭代器对象,否则会抛出异常


ins = C()
iter(ins)       #通过对该对象调用iter函数自动调用__iter__

#输出结果:__iter__方法被调用
from collections.abc import Iterable

class C1:
    def __iter__(self):		#定义魔法方法__iter__
        pass

class C2:
    pass

ins1 = C1()
ins2 = C2()
print(isinstance(ins1, Iterable))
print(isinstance(ins2, Iterable))

#输出结果:
"""
True
False
"""

6.next()

自动被调用的场景
对该对象调用next函数的时候,会被自动调用

作用
在类中同时定义了__iter__方法和__next__方法,这个类创建出来的对象就是一个迭代器,一般来说,该方法需要返回迭代器中的数据,具体的案例请移步Python——迭代器

案例

class C:
    def __next__(self):         #定义魔法方法__next__
        print('__next__方法被调用')


ins = C()
next(ins)       #通过对该对象调用next函数自动调用__next__

#输出结果:__next__方法被调用
from collections.abc import Iterator

class C1:
    def __iter__(self):
        pass

    def __next__(self):		#定义魔法方法__next__
        pass

class C2:
    pass

ins1 = C1()
ins2 = C2()
print(isinstance(ins1, Iterator))
print(isinstance(ins2, Iterator))

#输出结果:
"""
True
False
"""

7.str()

自动被调用的场景
在某些需要将对象转为字符串的时候被自动调用,比如打印的时候、将在格式化字符串中将对象转化为字符串的时候

作用
不对该方法进行重写,会打印(或者将对象转化为)<模块名.类名 object at 对象id> ,如果进行了重写,会打印(或者将对象转化为)__str__方法的返回值

值得注意的是__str__返回值必须为字符串类型对象,否则会引发异常

案例

class C:
    def __init__(self, data):
        self.data = data

ins = C(10)
print(ins)          #不重写__str__方法就会输出<模块名.类名 object at 对象id>

#输出结果:<__main__.C object at 0x0000024CC5DAA850>
class C:
    def __init__(self, data):
        self.data = data

    def __str__(self):              #重写魔法方法__str__
        print('__str__方法被调用')
        return  str(self.data)      #__str__必须要返回一个字符串对象,否则会抛出异常


ins = C(10)
print(ins)          #通过打印实例对象,自动调用__str__

#输出结果:
"""
__str__方法被调用
10
"""
class C:
    def __init__(self, data):
        self.data = data

    def __str__(self):              #重写魔法方法__str__
        print('__str__方法被调用')
        return  str(self.data)      #__str__必须要返回一个字符串对象,否则会抛出异常


ins = C(10)
print(f'{ins}')          #通过将实例对象转化为字符串对象,自动调用__str__

#输出结果:
"""
__str__方法被调用
10
"""

8.getitem()

自动被调用的场景
在使用索引获取数据的时候,会被自动调用

作用
使得对象可以通过下标取出数据

值得注意的是要设置一个形参接收指定的下标值

案例

class C:
    def __init__(self):
        self.lst = [1, 2, 3, 4]

    def __getitem__(self, key):		#定义魔法方法__getitem__
        print('__getitem__方法被调用')
        return self.lst[key]

ins = C()
print(ins[1])

#输出结果:
"""
__getitem__方法被调用
2
"""
class C:
    def __init__(self):
        self.dct = {'a':1, 'b':2, 'c':3, 'd':4}

    def __getitem__(self, key):		#定义魔法方法__getitem__
    	print('__getitem__方法被调用')
        return self.dct[key]

ins = C()
print(ins['b'])

#输出结果:
"""
__getitem__方法被调用
2
"""

9.setitem()

自动被调用的场景
在使用索引修改(或创建)数据的时候,会被自动调用

作用
使得对象可以通过下标修改(或创建)数据

值得注意的是要设置两个形参分别接收指定的下标值和数据

案例

class C:
    def __init__(self):
        self.dct = {'a':1, 'b':2, 'c':3, 'd':4}

    def __setitem__(self, key, value):		#定义魔法方法__setitem__
        self.dct[key] = value

ins = C()
print(ins.dct)
ins['b'] = 20
print(ins.dct)

#输出结果:
"""
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
__setitem__方法被调用
{'a': 1, 'b': 20, 'c': 3, 'd': 4}
"""
class C:
    def __init__(self):
        self.dct = {'a':1, 'b':2, 'c':3, 'd':4}

    def __setitem__(self, key, value):		#定义魔法方法__setitem__
        print('__setitem__方法被调用')
        self.dct[key] = value

ins = C()
print(ins.dct)
ins['e'] = 5
print(ins.dct)

#输出结果:
"""
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
__setitem__方法被调用
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
"""

10.delitem()

自动被调用的场景
在使用索引删除数据的时候,会被自动调用

作用
使得对象可以通过下标删除数据

值得注意的是要设置一个形参接收指定的下标值

案例

class C:
    def __init__(self):
        self.dct = {'a':1, 'b':2, 'c':3, 'd':4}

    def __delitem__(self, key):		#定义魔法方法__delitem__
        print('__delitem__方法被调用')
        del self.dct[key]

ins = C()
print(ins.dct)
del ins['a']
print(ins.dct)

#输出结果:
"""
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
__delitem__方法被调用
{'b': 2, 'c': 3, 'd': 4}
"""

11.getattribute()(属性访问拦截器)

自动被调用的场景
在要进行任何属性(无论该属性是否存在,无论是访问实例属性还是类属性)(此处的属性也包括了方法)的访问时,都会自动调用

作用
如果对该方法进行了重写,则返回值作为属性访问得到的结果值,如果对应的类对象中没有定义该方法,则正常返回所访问的属性

通过该方法,可以实现对所要访问的属性进行检测,针对不同的属性,进行不同的操作,从而实现对某一部分特殊属性访问的拦截
在实际应用中,该方法可以实现对某一个属性被访问次数和具体访问时间的记录

值得注意的是要设置一个形参接收所要访问的属性名(一个字符串类型对象)

案例

class C:
    def __init__(self):
        self.data_1 = 10
        self.data_2 = 20

    def __getattribute__(self, name):           #重写__getattribute__方法
        print('__getattribute__方法被调用')
        if name == 'data_1':              #当要访问的属性名为'data_1',允许访问
            return super().__getattribute__(name)		#通过调用父类的同名方法,返回数据
        else:                             #当要访问的属性名为'data_2',不允许访问
            return '该属性无法被访问'

ins = C()
print(ins.data_1)
print(ins.data_2)

#输出结果:
"""
__getattribute__方法被调用
10
__getattribute__方法被调用
该属性无法被访问
"""

12.getattr()

自动被调用的场景
与__getattribute__类似,只不过魔法方法__getattribute__对所有的属性(无论是否存在)都会进行拦截(即__getattribute__为全部拦截),而魔法方法__getattr__只会对一个不存在属性(即实例对象中、类对象中、父类中…都没有该同名属性)进行访问的时候会被自动调用(即__getattr__为部分拦截)

作用
如果对该方法进行了重写,则返回值作为属性访问得到的结果值,如果对应的类对象中没有定义该方法,则抛出异常

在实际应用中,该方法可以实现对所要访问的不存在属性的时间和该属性名进行记录

值得注意的是要设置一个形参接收所要访问的属性名(一个字符串类型对象)而且__getattr__和__getattribute__同时使用的时候,只会触发__getattribute__

案例

class C:
    def __init__(self):
        self.data_1 = 10

    def __getattr__(self, name):          #定义__getattr__方法
        print('__getattr__方法被调用')
        return '该属性不存在'

ins = C()
print(ins.data_1)
print(ins.data_2)

#输出结果:
"""
10
__getattr__方法被调用
该属性不存在
"""
class C:
    def __init__(self):
        self.data_1 = 10

    def __getattribute__(self, name):
        print('__getattribute__方法被调用')     
        if name == 'data_1':
            return super().__getattribute__(name)
        else:
            return '该属性不存在'

    def __getattr__(self, name):          #定义__getattr__方法
        print('__getattr__方法被调用')
        return '该属性不存在'

ins = C()
print(ins.data_1)
print(ins.data_2)

#输出结果:
"""
__getattribute__方法被调用
10
__getattribute__方法被调用
该属性不存在
"""

13.del()

自动被调用的场景
当一个对象被回收的时候,会被自动调用

作用
在一个对象被回收的时候,

这个方法的调用时刻是不好判断的,因为一个对象的引用计数为0的时候会自动被回收,但是使得引用计数增加或者减少的方法有很多

值得注意的是__del__并不是在执行del的时候被自动调用的,即del操作与__del__关联性不大,del操作仅仅就是减少引用计数而已

案例

class C:
    def __init__(self):
        self.data_1 = 10

    def __del__(self):		#定义魔法方法__del__
        print('__del__方法被调用')
    
ins = C()		#此时实例对象的引用计数为1
a = ins			#此时实例对象的引用计数为2

del ins			#此时实例对象的引用计数为1
print('del操作结束')

#最后程序运行结束,所有对象都被回收,自动调用__del__

#输出结果:
"""
del操作结束
__del__方法被调用
"""
class C:
    def __init__(self):
        self.data_1 = 10

    def __del__(self):
        print('__del__方法被调用')
    
ins = C()		#此时实例对象的引用计数为1
a = ins			#此时实例对象的引用计数为2

del ins			#此时实例对象的引用计数为1
del a			#此时实例对象的引用计数为0,对象被回收,自动调用__del__
print('del操作结束')

#输出结果:
"""
__del__方法被调用
del操作结束
"""
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值