这周比较忙,趁周末有空更新一篇文章,本文主要讲解类中实例赋值涉及的几种场景。
方法一;赋值即定义
场景:常用方式
样例:self.name = laokoo
方法二;setattr函数赋值
场景:使用字符串的方式操作实例的属性
样例: setattr(self,'name','laokoo')
方法三;实例字典赋值
场景:非标方式,本质上实例就是通过这种方式储存属性的
样例:self.__dict__[name] = 'laokoo'
方法四;类中实现__setattr__魔术方法
场景:=号赋值的方式,setattr函数的赋值本质上都是调用__setattr__魔术方法进行赋值。
__setattr__魔术方法中调用基类的该方法可以实现self属性的赋值
__setattr__魔术方法中使用实例字典赋值的方式可以实现self属性赋值
__setattr__魔术方法中如果使用=号赋值或者setattr函数会引起无限递归
样例:详见下方代码
方法五;类属性是另外一个类的实例,该类属性也是实例的属性
场景:实例访问类属性会触发描述器调用装饰类__get__方法的返回值
实例通过=号赋值会触发描述器调用装饰类的__set__方法进行属性设置
样例:详见下方代码
方法六,类中实现__slots__属性
场景:类中实现该方法后会禁用掉实例的字典,实例的属性全部通过类属性进行储存。
该属性主要针对实例属性过多,内存资源不足,实例属性较为简单的场景
__slots__不支持动态添加属性,__slots__不支持类继承
建议:可以使用tracemalloc模块测试元组和字典内存空间占用效果
样例:详见下方代码
备注:方法六中我打印了实例属性"member 'name' of 'Person' objects"
类型是"class 'member_descriptor'"。小弟其实不太明白__slots__
后端的实现原理,例如__slots__是用什么数据类型存放数据,member_descriptor
类中不同实例同一个属性是如何存储的。惯例先上汇总说明,后上代码参考。为了方便大家测试,我将测试区代码封装成了多个小块,大家在测试时根据场景不同区分调用即可。
此外本次测试中描述器方法,反射方法,slots方法可能会相互干扰,所以大家如果有测试需求请测试时关闭掉存在干扰的选项(存在干扰的我已经在文中进行了说明),另外大家如果不太明白描述器原理也可以查看我之前整理的一篇关于描述器的说明。
class Face:
def __get__(self, instance, owner):
print('in get ~~~')
return self
# 装饰类必须实现set魔术方法才能影响功能类的赋值语句
def __set__(self, instance, value):
print('in set ~~~~')
self.data = value
def __repr__(self):
return ''
class Person:
face = Face()
# #最后测试再开启,slots会禁用掉实例的字典,所以代码中只要使用到实例__dict__的部分都会报错。测试该功能前请关闭dict使用项
# __slots__ = ['name','age']
def __init__(self, name):
print('in init ~~~~')
self.name = name
# 观察face的值,观察实例的字典
self.face = 'myface'
# 第一阶段我们不设置该魔术方法,第二阶段开启该魔术方法
def __setattr__(self, key, value):
print('{} {} in setattr ~~~'.format(key, value))
print(self.__dict__)
# 测试一,调用基类方法观察效果
super().__setattr__(key, value)
# 测试二,直接使用实例字典赋值观察效果
# self.__dict__[key] = value
# 测试三,直接使用实例赋值观察效果
# self.key = value
# 测试四,使用setattr函数赋值观察效果
# setattr(self,key,value)
print(self.__dict__)
def test_show(user_input: int):
"""为了方便观察,我将一个测试场景进行了打印区分"""
if user_input == 1:
"""第一阶段使用方式一进行测试
测试方法:通过=的方式直接赋值
观察结果:name的值,实例的字典"""
laokoo = Person('laokoo')
print(laokoo.name)
laokoo.name = 'kabu'
print(laokoo.__dict__)
print(laokoo.name)
elif user_input == 2:
"""第一阶段使用方式二进行测试
测试方法:setattr()函数赋值
观察结果:name的值,实例的字典"""
laokoo = Person('laokoo')
print(laokoo.name)
setattr(laokoo, 'name', 'kabu')
print(laokoo.__dict__)
print(laokoo.name)
elif user_input == 3:
"""第一阶段使用第三种方式测试
测试方法:使用实例字典直接赋值(非标)
观察结果:name的值,实例字典"""
laokoo = Person('laokoo')
print(laokoo.name)
laokoo.__dict__['name'] = 'kabu'
print(laokoo.__dict__)
print(laokoo.name)
elif user_input == 4:
"""第二阶段使用第四种方式测试,这里的知识点比较多
测试方式:直接赋值,setattr函数赋值,这两种方法在__setattr__中测试
观察结果:name的值,__setattr__方法执行,实例字典"""
laokoo = Person('laokoo')
"""观察实例的初始化逻辑,先调用init,然后遇见赋值语句,直接调用__setattr__魔术方法,然后将key,value传入
因为我们的赋值动作最终实现效果是往实例的字典添加键值对,所以该魔术方法最终也会执行该操作"""
print(laokoo.name)
elif user_input == 5:
"""第三阶段使用第五种测试,这里会用到描述器,建议关闭掉功能类的setattr方法,不然显示内容太多
测试方法:直接赋值,setattr函数赋值
观察结果:face的值"""
laokoo = Person('laokoo')
print(laokoo.face)
# 观察data是否由值输出
print(laokoo.face.data)
elif user_input == 6:
"""第四阶段使用第六种测试,这里会用到__slots__
"""
laokoo = Person('laokoo')
setattr(laokoo,'age',18)
# setattr(laokoo,'x','xx')
#实例字典会被禁用,会使用类字典储存属性值
# print(laokoo.__dict__)
#name字段对应的值是'name': (类型),
print(Person.__dict__)
#开启__slot__后name才会切换到类属性
# print(type(Person.name))
| 留言与评论(共有 0 条评论) “” |