Python Class 对象或类型通过内置成员 dict 来存储成员信息。
还可以通过重载 getattr 和 setattr 来拦截对成员的访问,需要注意的是 getattr 只有在访问不存在的成员时才会被调用。
如果类型继承自 object,我们可以使用 getattribute 来拦截所有(包括不存在的成员)的获取操作
dict 返回类的属性字典, 会调用getattrbute
如果有一个类A;
a = A()
a.__dict__ 和 A.__dict__会有所不同, a__dict__中没有方法,而 A.__dict__ 有方法。
所以可见 类方法是类的属性被所有对像共享。
python的对象可以动态加属性,例如 a.ss=3, 这时a.__dict__就会有ss的key, 而A.__dict__ 不会有这个key,从而引申出类属性和对象属性(这里不做过多解释)。
这里要问一个问题了:
对象的dict中没有方法, 那对象是怎么调用方法的呢?
在这里就引申出Descriptor个东西:
class RevealAccess(object):
def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name
def __get__(self, obj, objtype):
print 'Retrieving', self.name
return self.val
def __set__(self, obj, val):
print 'Updating' , self.name
self.val = val
在类中定义get和set方法的,成为descriptor
它是用来做什么的呢。。。。。
再写一个类:
class MyClass(object):
x = RevealAccess(10, 'var "x"')
def __init__(self):
self.y = 5
def test(self):
print 'hello world!'
test = RevealAccess(test, 'var "x"')
if __name__ == "__main__":
m = MyClass()
print m.y
# 输出
# 5
print m.x
# 输出
# Retrieving var "x"
# 10
print m.test
# 输出
# Retrieving var "x"
# <function test at 0x02CBE870>
哈哈, 结果看出来了吧;当对像调用x(属性),test(方法)的时候都调用了get方法。
这个RevealAccess的对象就是一个descriptor。访问属性m.x就是调用get方法,设置属性值就是调用set方法。还可以有一个delete方法,在del m.x时被调用。
下一个问题:
print MyClass.__dict__["test"] # 输出: <function test at 0x02C9E8F0>
print m.test # 输出: <bound method MyClass.test of <__main__.MyClass object at 0x02C9DB70>>
这是什么情况, 这俩明显不是一个东西, 什么情况。
这说明test方法在类中仅仅是一个function, 他跟对象还是没关系。
print MyClass.__dict__["test"].__get__(m, Myclass) # 输出: <bound method MyClass.test of <__main__.MyClass object at 0x02CDDB90>>
这回是一个东西了,其实,类的成员函数就是一个descriptor,在实例化对象m的时候,做了m.test = MyClass.__dict__["test"].__get__(m, Myclass) 这么一件事,这回通了。哈哈。
有了这个基础: 理解一下staticmethod和classmethod这两个decorator,staticmethod就是像RevealAccess一样忽略第一个参数,直接返回参数。classmethod就是手动赛一个对像进去:
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
def newfunc(*args):
return self.f(klass, *args)
return newfunc
dict 妙用
class Person:
def __init__(self,_obj):
self.name = _obj['name']
self.age = _obj['age']
self.energy = _obj['energy']
..............
class Person:
def __init__(self, _obj):
self.__dict__.update(_obj)
这样就减少了很多代码量
delattr: 当使用 p = Person(); del p.name 会调用
主要参考http://hbprotoss.github.io/posts/python-descriptor.html
http://blog.jobbole.com/21351/