What is the difference between a function, an unbound method and a bound method?
我问这个问题是因为对这个答案的评论线索进行了讨论。我有90%的方法可以让我的头脑清醒过来。
1 2 3 4 | In [1]: class A(object): # class named 'A' ...: def f1(self): pass ...: In [2]: a = A() # an instance |
1 2 3 4 5 6 7 8 | In [3]: a.f1 # a bound method Out[3]: <bound method a.f1 of <__main__.A object at 0x039BE870>> In [4]: A.f1 # an unbound method Out[4]: <unbound method A.f1> In [5]: a.__dict__['f1'] # doesn't exist KeyError: 'f1' In [6]: A.__dict__['f1'] # a function Out[6]: <function __main__.f1> |
绑定方法、未绑定方法和函数对象之间有什么区别,它们都由F1描述?如何称呼这三个物体?他们怎么能互相转化呢?关于这些东西的文档很难理解。
函数是由
1 2 | def f1(self): pass |
这里,
1 2 | class C(object): f1 = f1 |
现在,
1 2 3 4 | >>> C.f1 <unbound method C.f1> >>> C.f1.im_func is f1 True |
我们还可以使用
1 2 3 | >>> C2 = type('C2', (object,), {'f1': f1}) >>> C2.f1 <unbound method C2.f1> |
我们可以手动将
1 2 3 | >>> import types >>> types.MethodType(f1, None, C) <unbound method C.f1> |
未绑定的方法由类实例上的访问绑定:
1 2 | >>> C().f1 <bound method C.f1 of <__main__.C object at 0x2abeecf87250>> |
访问被转换为通过描述符协议调用:
1 2 | >>> C.f1.__get__(C(), C) <bound method C.f1 of <__main__.C object at 0x2abeecf871d0>> |
结合这些:
1 2 | >>> types.MethodType(f1, None, C).__get__(C(), C) <bound method C.f1 of <__main__.C object at 0x2abeecf87310>> |
或直接:
1 2 | >>> types.MethodType(f1, C(), C) <bound method C.f1 of <__main__.C object at 0x2abeecf871d0>> |
函数和未绑定方法的主要区别在于,后者知道它绑定到哪个类;调用或绑定未绑定方法需要其类类型的实例:
1 2 3 4 5 6 7 8 | >>> f1(None) >>> C.f1(None) TypeError: unbound method f1() must be called with C instance as first argument (got NoneType instance instead) >>> class D(object): pass >>> f1.__get__(D(), D) <bound method D.f1 of <__main__.D object at 0x7f6c98cfe290>> >>> C.f1.__get__(D(), D) <unbound method C.f1> |
由于函数和未绑定方法之间的差异非常小,所以python3就不需要区分了;在python3中,访问类实例上的函数只需提供函数本身:
1 2 3 4 | >>> C.f1 <function f1 at 0x7fdd06c4cd40> >>> C.f1 is f1 True |
在python2和python3中,这三个是等效的:
1 2 3 | f1(C()) C.f1(C()) C().f1() |
将一个函数绑定到一个实例具有将其第一个参数(通常称为
1 2 | (lamdba *args, **kwargs: f1(C(), *args, **kwargs)) functools.partial(f1, C()) |
is quite hard to understand
好吧,这是一个很难的主题,它与描述符有关。
让我们从函数开始。这里一切都很清楚-您只需调用它,在执行它时传递所有提供的参数:
1 2 3 | >>> f = A.__dict__['f1'] >>> f(1) 1 |
当参数个数有问题时,提出常规
1 2 3 4 | >>> f() Traceback (most recent call last): File"<stdin>", line 1, in <module> TypeError: f1() takes exactly 1 argument (0 given) |
现在,方法。方法是带有一些香料的函数。描述符出现在游戏中。如数据模型所述,
在
1 2 3 4 5 6 7 8 9 | >>> f = A.f1 >>> f() Traceback (most recent call last): File"<stdin>", line 1, in <module> TypeError: unbound method f1() must be called with A instance as first argument (got nothing instead) >>> f(1) Traceback (most recent call last): File"<stdin>", line 1, in <module> TypeError: unbound method f1() must be called with A instance as first argument (got int instance instead) |
如果此检查成功,则以该实例作为第一个参数执行原始
1 2 | >>> f(A()) <__main__.A object at 0x800f238d0> |
注意,
1 2 | >>> f.im_self is None True |
在
1 2 3 4 5 | >>> f = A().f1 >>> f.im_self <__main__.A object at 0x800f23950> >>> f() <__main__.A object at 0x800f23950> |
因此,
函数对象是由函数定义创建的可调用对象。绑定和未绑定方法都是由点二进制运算符调用的描述符创建的可调用对象。
绑定和未绑定方法对象有3个主要属性:
当调用绑定方法时,它调用
有关详细信息,请参阅python 2和python 3文档。
我的解释如下。
类
Python 3:
1 2 3 4 5 6 7 | class Function(object): . . . def __get__(self, obj, objtype=None): "Simulate func_descr_get() in Objects/funcobject.c" if obj is None: return self return types.MethodType(self, obj) |
Python 2:
1 2 3 4 5 | class Function(object): . . . def __get__(self, obj, objtype=None): "Simulate func_descr_get() in Objects/funcobject.c" return types.MethodType(self, obj, objtype) |
如果从类或实例调用函数,则调用其
b.
今天我看到的一件有趣的事情是,当我将一个函数赋给一个类成员时,它就变成了一个未绑定的方法。例如:
1 2 3 4 5 6 7 8 9 | class Test(object): @classmethod def initialize_class(cls): def print_string(self, str): print(str) # Here if I do print(print_string), I see a function cls.print_proc = print_string # Here if I do print(cls.print_proc), I see an unbound method; so if I # get a Test object o, I can call o.print_proc("Hello") |