python 面向对象双下方法

双下方法

Python中有大量类似__init__这种以双下划线开头和结尾的双下方法,也可以叫做魔法方法,它们有着非常重要的地位和作用,也是Python语言独具特色的语法之一!

__str__ (实例对象被打印时执行)

此内置方法可以在实例对象被执行打印操作(print、前端打印等等)时候自动返回一个数据,这个数据只能为字符串的形式。通过这个内置方法可以更好的描述一个实例对象。

代码示例(不加__str__)

		class MyClass(object):
		    pass
		
		x = MyClass()
		print(x)

若不加双下str方法打印的结果是:  <__main__.MyClass object at 0x0000018B2F876B30>
代码示例(加__str__)

		class MyClass(object):
		    def __str__(self):
		        return 'from __str__'
		
		x = MyClass()
		print(x)


加了双下str方法打印结果是: from __str__
需要注意的是,返回的数据必须是字符串格式,且要用return返回,只用print会报错。

__del__ (实例对象被删除时执行)

此内置方法在实例对象被删除的时候执行,这个删除可以是主动删除或者是被动删除。

代码示例(被动删除)

		class MyClass(object):
		    def __del__(self):
		        print('from __del__')
		
		x = MyClass()
		print('xxx')

打印结果:  
		xxx
		from __del__
		
		
虽然在代码中并没有进行删除操作,但是python有一个垃圾回收机制,也就是说,不用的会被回收删除,所
以执行了 __del__ 里面的代码。
代码示例(主动删除)

		class MyClass(object):
		    def __del__(self):
		        print('from __del__')
		
		x = MyClass()
		del x  
		print('xxx')

打印结果
		from __del__
		xxx


此示例使用了del主动的将实例对象删除,此时和被动删除示例的打印结果不同的是 xxx 在最后了。也就
是说先执行了 __del__ 里的代码。

__getattr__ (实例对象查找不存在的属性时执行)

这个内置方法和前面文章中写的反射的方法 getattr 长得很相似。getattr 是用于获得类或对象中对应的变量名或者方法名没有返回None。而 __getattr__方法是在实例对象查找不存在的属性名时执行。

代码示例(不加__getattr__)

		class MyClass(object):
		    pass
		
		x = MyClass()
		print(x.name)

由于在类和实例化对象名称空间中均没有name属性,此时运行代码会报错找不到name:
		AttributeError: 'MyClass' object has no attribute 'name'
代码示例(加__getattr__)
		
		class MyClass(object):
		    def __getattr__(self, item):  # item 参数接收查找的属性名
		        print('from __getattr__')
		        return '没有找到属性时执行'
		
		x = MyClass()
		print(x.name)

打印结果
		from __getattr__
		没有找到属性时执行


此时没有找到name属性,执行__getattr__方法中的代码,也可以返回一个值给x.name。

__setattr__ (实例对象添加属性时执行)

此方法也和反射中的 setattr 类似,反射中的 setattr 是给类或者对象添加属性的,而内置方法__setattr__ 是在实例对象添加属性时执行。

代码示例一

		class MyClass(object):
		    def __setattr__(self, key, value):
		        print('from __setattr__')
		
		x = MyClass()
		x.name = 'xxx'  
		print(x.__dict__)

打印结果
		from __setattr__
		{}


1. 我们发现此时 __setattr__ 方法虽然在给实例化对象添加属性时执行了,但是在打印实例化对象的名称
空间的时候却没有发现该属性。
2. 因为我们此时相当于重写了 __setattr__ 方法,而原本凡是赋值操作都会触发它的运行,重写了就失去
了原有功能,我们可以使用 super 方法进行继承回来,或者使用名称字典的方式添加如下示例。
代码示例二

		class MyClass(object):
		    def __setattr__(self, key, value):
		        print('from __setattr__')
		        # self.key = value  # 会一直迭代死循环
		        self.__dict__[key] = value  # 使用名称字典的方式添加属性
		        # super().__setattr__(key, value)  # 或者重新继承object中的方法
		
		x = MyClass()
		x.name = 'xxx'
		print(x.__dict__)
		print(x.name)


打印结果
		from __setattr__
		{'name': 'xxx'}
		xxx


1. 在示例一中我们知道他的属性并没有被加进去,我们可以在方法 __setattr__ 中使用 __dict__ 的方
式来给实例化对象添加属性。也可以重新继承 object 中的 __setattr__ 方法。
2. 如果使用了 self.key = value 来添加属性的话,又会执行 __setattr__ 方法形成了一个死循环。
3. 我们也可以在 __setattr__ 方法中添加一些条件判断,例如 key != 'name'、key.islower()...

__getattr__、__setattr__ 练习题

练习题要求是让字典具备句点符查找值和设置值的功能
字典是不能像类调用属性一样直接点的形式就可以获得值,所以我们要进行修改。
代码示例

		class MyDict(dict):
		    def __getattr__(self, item):
		        return self.get(item)
		
		    def __setattr__(self, key, value):
		        self.__dict__[key] = value
		        super().__setattr__(key, value)
		# 由于继承了 dict ,如果不加参数那么 x 就是一个空字典
		x = MyDict({'name': 'XWenXiang', 'age': 12})  
		print(x.name)  # 打印键名 name
		x.name = 'x'  # 修改键名name
		print(x.name)  # 打印修改后的键名name
		print(x.school)  # 打印不存在的键名,由于是 get() 方法,所以返回 None

打印结果
		XWenXiang
		x
		None

1. 我们对字典功能进行修改,首先肯定要创建一个类继承字典。
2. 实例化类并传入一个字典,若是不传字典实例对象是一个空字典。
3. 我们可以先用点的方式来获取和修改值试一试,会报错,因为点的方式获取的属性并不存在,所以我们要
使用方法 __getattr__ 来执行属性不存在时的代码
4. 在由于实例对象是字典,所以在方法 __getattr__ 中用 get() 的方式来获取字典的值,并返回出去。
5. 此时就是实现了用点的方式获取字典里的值,同样的用点的方式修改字典的值用 __setattr__ 方法,
该方法在给实例对象赋值的时候执行。如果不用__setattr__ 方法虽然打印修改的结果变了,但是没有加到
字典里面
6. 在方法 __setattr__ 中用字典修改值的方式对实例对象修改即可。

__call__ (实例对象被加括号调用的时候执行)

我们知道实例对象是不能向函数名一样加括号调用的,调用就会报错。而__call__就是在对象加括号调用的时候执行,就不会报错了。

代码示例
		class MyClass(object):
		    def __call__(self, *args, **kwargs):
		        print('from __call__')
		
		x = MyClass()
		x()  # 实例对象加括号调用

打印结果:  from __call__
给实例对象加括号执行 __call__ 的代码。

__enter__ (实例对象执行 with 语句开始执行)

__exit__ (实例对象执行 with 语句结束执行)

这俩个内置方法在执行 with 上下文管理开始的时候执行,而且少一个就会报错。其中方法__enter__可以返回一个值,这个值被 with 语法中 as 后面的变量接收,返回什么 as 后的变量就是什么。而方法__exit__则是 with 语句结束的时候执行。

代码示例

		class MyClass(object):
		    def __enter__(self):  # with 语句开始时候执行
		        print('from __enter__')
		        return self  # 返回一个值到 as 后面的变量(这里使用的是 self )
		    def __exit__(self, exc_type, exc_val, exc_tb):  # with 结束的时候执行
		        print('from __exit__')

		x = MyClass()
		x.name = 'xxx'  # 给实例对象添加一个属性 name 
		with x as f:  # x 是实例对象,f 是方法 __enter__ 返回的值
		    print('这是实例对象:  ', f)  # 这里用 self 传给 f
		    print('这是实例对象中的属性:  ', f.name)

打印结果
		from __enter__
		这是实例对象:   <__main__.MyClass object at 0x000001A86A2454E0>
		这是实例对象中的属性:   xxx
		from __exit__


__exit__、 __enter__ 的练习题

补全下面的代码使其不报错

		class Context:
			pass
		with Context() as ctx:
			ctx.do_something()

1. 首先我们可以看到这是一个 with 语句,with 后的跟着的也是类加括号也就是一个实例,但是此时并没
有方法  __exit__ 和  __enter__,所以先创建这俩方法
2. 然后发现在 with 中的 ctx 调用了一个方法,在类中除了实例对象可以调用方法还有的就是用类名来调
用,但是上面的调用方式并没有传入参数,也就是说是用实例对象调用的,所以在方法 __exit__ 中返回实
例对象。
3. 创建方法 do_something() 即可
代码示例

		class Context:
		    def __enter__(self):
		        return self
		    def __exit__(self, exc_type, exc_val, exc_tb):
		        pass
		    def do_something(self):
		        pass
		
		with Context() as ctx:
		    ctx.do_something()


__getattribute__ (实例对象查找属性时执行)

这个代码和 __getattr__方法有点像,__getattr__是在查找不存在的属性时执行,而方法__getattribute__只要实例对象查找名字无论名字是否存在都会执行该方法

'''如果类中有__getattribute__方法 那么就不会去执行__getattr__方法'''
代码示例

		class MyClass(object):
		    def __init__(self, name):
		        self.name = name
		    def __getattr__(self, item):
		        print('from __getattr__')
		        return '查找不存在的属性时执行'
		    def __getattribute__(self, item):
		        print('from __getattribute__')
		        return '查找属性时就会执行'
		
		x = MyClass('xxx')
		print(x.name)  # 查找已存在的属性
		print(x.age)  # 查找不存在的属性

打印结果
		from __getattribute__
		查找属性时就会执行
		from __getattribute__
		查找属性时就会执行


在示例中,虽然有内置方法 __getattr__ ,也查找了不存在的属性,但是并没有执行方法 __getattr__,
而是只执行了方法 __getattribute__ 。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值