创建不可变对象
修改__setattr__创建不可变对象
setattr方法掌管已有属性的赋值,和新建属性的赋值。
下面重写了setattr方法,可以在常规操作下保证对象是不可变的。
class F1:
def __init__(self):
#因为这个类修改了setattr属性,所以不能调用“=”赋值,只能调用父类的setattr方法
super().__setattr__('name', '小明')
super().__setattr__('age', 10)
# super().__setattr__('sex', '男') #因为slots中没有sex,即使在初始化中创建也会报错
def __setattr__(self, key, value):
raise TypeError("__class__.name has no attribut 'name'".format(__class__=self.__class__, name=key))
f = F1()
# f.sex='男' # 这里无法添加新属性,因为本质上调用的是__setattr__方法
f.__dict__['sex'] = '女' # 这里可以改变,因为这里调用的是字典的setattr,而不是当前这个对象的。
_slots_
slots使 __dict__对象失效, 不能添加新的属性。如果不重写 setattr方法,那么已有的属性是可变的。slots主要目的是限制属性数量节省内存。
以下为 : 让实例对象不可变,无法对已有属性重新赋值,无法创建新的属性和方法。
class F1:
__slots__ = ('name','age')
def __init__(self):
#因为这个类修改了setattr属性,所以不能调用“=”赋值,只能调用父类的setattr方法
super().__setattr__('name', '小明')
super().__setattr__('age', 10)
# super().__setattr__('sex', '男') #因为slots中没有sex,即使在初始化中创建也会报错
def __setattr__(self, key, value):
raise TypeError("__class__.name has no attribut 'name'".format(__class__=self.__class__, name=key))
f = F1()
# f.sex='男' #这里无法添加新的属性
# f.__dict__['sex'] = '女' # 这里不可以改变,因为slots使 dict属性失效了
slots 是不被继承的,所以子类是可以添加新的属性的
class F1:
# 这个类无法添加新的属性
__slots__ = ('name', 'age')
def __init__(self):
self.name = '小明'
self.age = 10
class F2(F1):
# slots属性不被继承,子类仍然使用dict管理属性。但是父类中声明的属性依然会使用slots管理。
pass
>> f = F2()
>> f.p = 'pp'
>> print(f.__dict__)
>> {'p': 'pp'}
>> print(f.__slots__) # 此时__slots__的返回值为父类中声明的值
>> ('name', 'age')
如果子类也需要slots的功能,那么需要显式声明。
class F1:
__slots__ = ('name', 'age')
def __init__(self):
self.name = '小明'
self.age = 10
class F2(F1):
# 这时F2也无法添加新的属性
__slots__ = ('sex',)
def __init__(self):
self.sex = '男'
super().__init__()
>> f = F2()
>> print(f.__slots__)
>> ('sex',)
>> print(f.__class__.__bases__[0].__slots__) # 这是获取父类的slots值
>> ('name', 'age')
使用tuple子类创建不可变对象
class F1(tuple):
# 实例化的过程是先new开辟内存区域,后init
# 因为元组是不可变的,需要在初始化内存区域的时候就存入数据,所以不能使用init方法
def __new__(cls, *args, **kwargs):
# 记得return
return super().__new__(cls, ('小明', '小王', '小李'))
# 使用字典记录每个元素的位置,然后用索引获取元素
def __getattr__(self, item):
return self[{'xiaoming': 0, "xiaowang": 1, "xiaoli": 2}[item]]
# 不添加这个也可以,但是在使用“=”赋值时,会没有结果,容易误导
# 所以重写这个方法,直接抛出异常
def __setattr__(self, key, value):
raise AttributeError
f = F1()
# f.xiaoming = 2 # 不添加__setattr__方法的话这里会没有反应
print(f.xiaoming)
_getattribute_() 隐藏私有属性
当访问一个属性时, 先从 _dict_ 中或者 __slots__ 中查找, 如果属性没有找到,则调用 __getattr__ 函数。如果值是一个修饰符,那么对修饰符进行处理。
class F1:
def __init__(self):
super().__setattr__('name', '小明')
super().__setattr__('age', 10)
def __setattr__(self, key, value):
raise TypeError("__class__.name has no attribut 'name'".format(__class__=self.__class__, name=key))
# 如果访问的是私有属性,那么触发异常
def __getattribute__(self, item):
if item.startswith('_'): raise AttributeError
return super().__getattribute__(item)
# 这个函数将无法调用
def _private(self):
return None
f = F1()
print(f.name)
print(f._private()) # 这个函数将无法调用