Python descriptor - function and method
转载请标明出处(http://blog.csdn.net/lis_12/article/details/53495627).
properties
,methods
, static methods
,class methods
, and super()
都是基于描述符实现的。本篇文章了解下就好,不必深究,如果没学过描述符的话,建议研究下描述符…
准备知识
- 访问属性优先级: 类属性的数据描述符 > 实例属性 > 类属性的非数据描述符,非描述符的类属性 >
__getattr__()
; - 如果一个对象定义了
__get__()
方法,在属性访问时会覆盖默认行为,调用__get__()
;(这个对象要为类属性)
function and method
Python面向对象的特征是建立在函数上的,非数据描述符将二者完美的结合在了一起。
类的字典将类中的方法当做函数存储。在定义类的时候,方法通常用关键字 def
和 lambda
来声明,这和创建函数的方式是一样的。唯一的不同之处是方法的第一个参数用来表示实例,Python约定,这个参数通常是 self, 也可以是 this 或者其它任何名字。
个人认为 方法就是一种加了命名空间的特殊函数,命名空间就是实例。
为了支持方法调用,函数定义了 __get__()方法
,即当函数作为属性被访问时会调用function.__get__()
。所有的函数都是非数据描述符,它们返回绑定(bound)还是非绑定(unbound)方法取决于是被实例调用还是被类调用。
绑定方法: 函数中的第一个参数已经被设置成实例;
未绑定方法: 所有参数原封不动地传给原来的函数,包括第一个参数self;
class function(object):
. . .
def __get__(self, obj, objtype = None):
"Simulate func_descr_get() in Objects/funcobject.c"
return types.methodType(self, obj, objtype)
因为function为非数据描述符,当function对象作为属性被访问时会调用function.__get__()
。
class Foo(object):
def __init__(self):
self.x = 1
def fun(self):
print 'fun'
def f1():
pass
f = Foo()
方法在类字典中的存储方式为函数
print f1 #<function fun at> print type(f1) #<type 'function'> print Foo.__dict__['fun'] #<function fun at>,与f1的结果一样,虽然是类中的方法,但是按照函数存储的 print type(Foo.__dict__['fun']) == type(f1) #True,相等啊,记住方法是按照函数存储的...
此时
Foo.__dict__['fun']
就是一个函数…只不过第一参数要传Foo的实例,不然可能会出现异常。按函数的形式调用方法
print Foo.__dict__['fun'](f) #fun,将实例以参数的形式传给fun函数,等价于f.fun()
绑定方法和非绑定方法
print f.fun #bound method,此时已经将第一个参数设置成了实例f print Foo.fun #unbound method,参数原封不动 f.fun() #调用fun方法 #Foo.fun() #error,缺少实例参数,即self Foo.fun(f) #加上实例参数,等价于f.fun(),有没有感觉很和函数一样...,只不过多了个命名空间
函数为非数据描述符
print '__get__' in dir(f1) #True print '__get__' in dir(Foo.__dict__['fun']) #True
类中的函数作为属性被访问时,描述符方法
__get__()
会将函数转化为方法,即当调用f.fun时,编译器会将f.fun
转化为Foo.__dict__['fun'].__get__(f,type(f))
,也就是说f.fun为Foo.__dict__['fun'].__get__(f,type(f))
的返回值。bound = Foo.__dict__['fun'].__get__(f,type(f)) bound1 = Foo.__dict__['fun'].__get__(f) unbound = Foo.__dict__['fun'].__get__(None,Foo) print bound == f.fun #True print bound1 == f.fun #True print unbound == Foo.fun #True
由以上代码可知,
方法在类字典中的存储方式为函数;
绑定方法和非绑定方法是两个不同的类型。
绑定方法,由函数转化为绑定方法时,函数的第一个参数设置成实例,其余的参数为绑定方法的参数;
非绑定方法,由函数转化为非绑定方法时 函数中的参数原封不动的传给方法。
函数为非数据描述符,当类中的函数作为属性被访问时(即访问类中的方法),会调用
function.__get__()
,function.__get__()
的返回值为方法;
函数和方法测试code
class Foo(object):
def __init__(self,x = 1):
self.x = x
def fun(self,x):
print 'fun:self.x = %s;x = %s'%(self.x,x)
def fun():
pass
f = Foo()
f.fun(2) #fun:self.x = 1;x = 2
#传参二重奏,先将实例传进去,然后再将其他参数传进去
Foo.__dict__['fun'].__get__(f,type(f))(2) #fun:self.x = 1;x = 2,等价于f.fun(2)
#验证
F = type(f).__dict__['fun'].__get__(f,type(f))
print type(F) #<type 'instancemethod'>
print F == f.fun #True
F(3) #fun:self.x = 1;x = 3
以下为个人理解.
传参二重奏:第一阶段先给self赋值,第二阶段给除self以外的参数赋值…
fun
中的self
参数为fun
所在的命名空间,实例f
就是一个命名空间;F = type(f).__dict__['fun'].__get__(f,type(f))
先给fun中的self赋值
,相当于传参只传了一部分,即只给self赋值了(self = f),其他参数未赋值,此时F就为一个特殊的
普通函数,可以像普通函数一样调用…- 继续给其他参数赋值,即给除了self之外的参数赋值,如调用函数F,
F(3)
,
静态方法
那些不需要 self
和cls
变量的方法适合为静态方法。
staticmethod()
Python的模拟实现:
class staticmethod(object):
"Emulate Pystaticmethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, objtype=None):
return self.f
因为staticmethod为非数据描述符,当staticmethod对象作为属性被访问时会调用staticmethod.__get__()
class E(object):
def f(x):
print x
f = staticmethod(f)
静态方法的存储方式
print E.__dict__['f'] #<staticmethod object at>
静态方法与实例方法的存储方式不一样,不是按照函数形式存储的。
从类和实例中调用静态方法
e = E() E.f(3) #3 e.f(3) #3 #E.__dict__['f'](3) #TypeError: 'staticmethod' object is not callable,不能利用这种方式调用
静态方法是非数据描述符
print '__get__' in dir(E.__dict__['f']) #True
静态方法作为属性被访问时会调用
staticmethod.__get__()
'''第一个参数为实例还是None对静态方法没影响,只要类对了就ok了''' a = E.__dict__['f'].__get__(None,E) b = E.__dict__['f'].__get__(e,E) print a == E.f #True print b == E.f #True
从以上代码可知,
- 静态方法在类字典中的存储方式与实例方法不同;
- 静态方法为非数据描述符,当静态方法作为属性被访问时会调用
staticmethod.__get__()
; - 静态方法与实例无关,与类有关…
类方法
与静态方法不同,类方法的第一个参数用来表示类,一般为cls。
classmethod()
Python模拟实现:
class classmethod(object):
"Emulate Pyclassmethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj) #注意这里...
def newfunc(*args):
return self.f(klass, *args)
return newfunc
因为classmethod为非数据描述符,当classmethod对象作为属性被访问时会调用classmethod.__get__()
。
class E(object):
def f(cls,x):
print x
f = classmethod(f)
e = E()
类方法的存储方式
print E.__dict__['f'] #<classmethod object at 0x00000000033FB168>
类方法的存储方式与静态方法,实例方法都不同。
从类和实例调用类方法
e.f(3) #3 E.f(3) #3 #E.__dict__['f']() #TypeError: 'classmethod' object is not callable,不能用这种方式调用
与实例方法的self一样,cls参数已经自动传入类方法了,无需手动传入。
类方法是非数据描述符
print '__get__' in dir(E.__dict__['f']) #True
类方法作为属性被访问时会调用
classmethod.__get__()
a = E.__dict__['f'].__get__(None,E) #<bound method type.f of <class '__main__.E'>> b = E.__dict__['f'].__get__(e,E) #<bound method type.f of <class '__main__.E'>> c = E.__dict__['f'].__get__(E,E) #<bound method type.f of <class '__main__.E'>> d = E.__dict__['f'].__get__(E) #<bound method type.f of <type 'type'>> print a == E.f #True print b == E.f #True print c == E.f #True print d == E.f #False
由上述代码可知,
- 类方法的存储方式与静态方法,实例方法都不同;
- 类方法也为非数据描述符,当类方法作为属性被访问时会调用
classmethod.__get__()
; - 类方法与实例无关,与类有关…
类方法相比于静态方法的优势
当一个函数不需要相关的数据做参数而只需要一个类的引用的时候,这个特征就显得很有用了。类方法的一个用途是用来创建不同的类构造器。在Python 2.3中,
dict.fromkeys()
可以依据一个key列表来创建一个新的字典。等价的Python实现就是:
class Dict: . . . def fromkeys(klass, iterable, value=None): "Emulate dict_fromkeys() in Objects/dictobject.c" d = klass() for key in iterable: d[key] = value return d fromkeys = classmethod(fromkeys) #现在,一个新的字典就可以这么创建: >>> Dict.fromkeys('abracadabra') {'a': None, 'r': None, 'b': None, 'c': None, 'd': None}
类方法,静态方法,实例方法
class Foo(object):
def inst_f(self,a):
print 'inst_fun()',a
@classmethod
def class_f(cls,a):
print 'class fun()',a
@staticmethod
def static_f(a):
print 'static fun()',a
f = Foo()
存储方式
print type(Foo.__dict__['inst_f']) #<type 'function'> print type(Foo.__dict__['class_f']) #<type 'classmethod'> print type(Foo.__dict__['static_f']) #<type 'staticmethod'>
以函数的形式调用(从类调用方法)
Foo.inst_f(f,1) #inst_fun() 1,等价于f.inst_f() Foo.class_f(2) #class fun() 2,等价于f.class_f(2) Foo.static_f(3) #static fun() 3,等价于static_f(3)
从实例调用方法
f.inst_f(1) #inst_fun() 1,等价于f.inst_f() f.class_f(2) #class fun() 2,等价于f.class_f(2),self自动传入 f.static_f(3) #static fun() 3,等价于static_f(3),cls自动传入
从类和实例调用区别
print f.inst_f,Foo.inst_f #bound method,unbound method print f.inst_f == Foo.inst_f #False print f.class_f,Foo.class_f #bound method,bound method,两种形式是一样的 print f.class_f == Foo.class_f #True print f.static_f,Foo.static_f #function static_f,function static_f,两种是一样的 print f.static_f == Foo.static_f #True
从以上代码可知,
1) 调用实例方法时,从类和实例调用是不一样的,即实例方法与实例有关;
2) 调用类方法和静态方法时,从类和实例调用是一样的,即类方法和静态方法与实例无关。
利用
__get__()
调用方法Foo.__dict__['inst_f'].__get__(f,type(f))(1) #inst_fun() 1 Foo.__dict__['static_f'].__get__(None,type(f))(2) #static fun() 2 Foo.__dict__['class_f'].__get__(None,type(f))(3) #class fun() 3