在没使用__slots__的情况下,__new__会为每个实例创建一个__dict__字典,来保存实例的所有属性。在进行属性寻找的时候,会先在对象的__dict__中找,找不到,就去类的__dict__中找,再找不到就去父类的__dict__中找,。。
类是创建对象的模版。在静态类型语言中,对象只能拥有类中定义的那些属性。而在Python中,可以在运行时,随意的增加或删除对象的属性。这就很可能造成:运行时,为对象增加了很多属性,占用了很多的内存。为了避免类似的现象,Python的新式类提供了__slots__类属性,用来限制类的对象所能拥有的属性。
# coding: utf8
class NonSlots(object):
pass
class Slots(object): # 一定要是新式类
__slots__ = ("a", "b") # 属性名列表
if __name__ == "__main__":
non_slots = NonSlots()
non_slots.c = "c"
slots = Slots()
slots.a = "a" # 因为a在__slots__中,所以不会出异常
slots.c = "c" # 此处会出AttributeError
默认情况下,__new__会为每一个实例创建一个属性字典__dict__,用来保存实例的所有属性。但是当使用__slots__时,__new__不会为实例创建该字典。
当父类没有__slots__时,子类仍然有__dict__,__slots__之外的属性,会被保存到属性字典中。此时,__slots__的意义,也就不大了。
当父类也有__slots__时,子类的__slots__的值,可以看成是自己的__slots__ 加上 父类的__slots__。
有三个方法:
__get__(self, obj, objtype=None) --> value
__set__(self, obj, value) --> None
__delete__(self, obj) --> None
重写了其中一个或多个方法的对象,就是描述符。
注意:
object.__getattribute__方法中,被调用的。重写__getattribute__方法,可能会阻止描述符的自动调用
__get__方法;设置描述符时,会自动调用__set__方法;删除描述符时,会自动调用__delete__方法
Python中的很多高级特性,都是通过描述符实现的。比如,staticmethod、classmethod等。
下面看一个自己实现classmethod的例子:
[root@iZj6chejzrsqpclb7miryaZ ~]# cat test.py
# coding: utf8
from functools import partial
class MyClassMethodDescriptor(object):
def __init__(self, func):
self._func = func
def __get__(self, obj, objtype):
# obj:访问描述符的实例
# objtype:等价于 type(obj)
print "{obj: %r, objtype: %r}" % (obj, objtype)
return partial(self._func, objtype)
def __set__(self, obj, val):
# obj:设置描述符的实例
# val:要设置成的值
pass
def __del__(self, obj):
# obj:删除描述符的实例
pass
def func(cls):
print "cls is: %r" % cls
print "func() is invoked..."
class Test(object):
my_classmethod = MyClassMethodDescriptor(func)
test = Test()
test.my_classmethod()
[root@iZj6chejzrsqpclb7miryaZ ~]# python test.py
{obj: <__main__.Test object at 0x7fe9b99c54d0>, objtype: }
cls is:
func() is invoked...
__getattribute__(self, attr_name)__getattr__(self, attr_name)方法
__getattr__(self, attr_name)attr_name的属性时,就会调用它的__getattr__(self, attr_name)方法
当使用内建函数getattr(obj, attr_name, ...)获取obj的属性时:
__getattribute__方法,如果出AttributeError,则调用__getattr__方法__getattr__方法需要注意的是:
在__getattribute__和__getattr__方法中,调用getattr(self, xxx)可能会造成死递归。一个解决方案是使用object.__getattribute__(self, attr_name)(新式类)。
__setattr__(self, name, value)当使用内建函数setattr(obj, name, value)设置对象的属性时,会调用__setattr__方法。所以在__setattr__方法中,调用setattr(self, ...)可能造成死递归。一个解决方案是使用属性字典__dict__。比如:
[root@iZj6chejzrsqpclb7miryaZ ~]# cat test.py
class Test:
def __setattr__(self, name, value):
print "__setattr__"
self.__dict__[name] = value
def __getattr__(self, name):
print "__getattr__"
raise AttributeError("has no attribute: %s" % name)
test = Test()
test.a = "a"
print test.a
[root@iZj6chejzrsqpclb7miryaZ ~]# python test.py
__setattr__
a
__delattr__方法 和 delattr内建函数:
[root@iZj6chejzrsqpclb7miryaZ ~]# cat test.py
class Test:
def __setattr__(self, name, value):
print "__setattr__"
self.__dict__[name] = value
def __getattr__(self, name):
print "__getattr__"
raise AttributeError("has no attribute: %s" % name)
def __delattr__(self, name):
print "__delattr__"
self.__dict__.pop(name)
test = Test()
test.a = "a"
print test.a
delattr(test, "a")
print getattr(test, "a", None)
[root@iZj6chejzrsqpclb7miryaZ ~]# python test.py
__setattr__
a
__delattr__
__getattr__
None