2011-06-25 256 views
21

根据我对Python's data model的理解,特别是“实例方法”小节,每当读取一个属性值为“用户定义函数”类型的属性时,会有一些魔法启动,并且会得到绑定实例方法,而不是实际的原始函数。这就是为什么当你调用一个方法时你没有明确地通过self参数的原因。将函数赋值给对象属性

但后来,我希望能够与具有相同签名的功能,以取代对象的方法:

class Scriptable: 
    def __init__(self, script = None): 
     if script is not None: 
      self.script = script # replace the method 
    def script(self): 
     print("greetings from the default script") 

>>> scriptable = Scriptable() 
>>> scriptable.script() 
greetings from the default script 

>>> def my_script(self): 
...  print("greetings from my custom script") 
... 
>>> scriptable = Scriptable(my_script) 
>>> scriptable.script() 
Traceback (most recent call last): 
    ... 
TypeError: script() takes exactly 1 positional argument (0 given) 

我创建的Scriptable一个实例,其script属性设置为用户使用单个参数定义函数,就像类中定义的那样。所以当我阅读scriptable.script属性时,我会期待这个魔法启动并给我一个不带参数的绑定实例方法(就像我没有替换script时那样)。相反,它似乎在回馈我传递的完全相同的功能,self参数和全部。方法绑定魔法没有发生。

当我在类声明中定义一个方法时,为什么方法绑定法术能够工作,但是当我分配属性时不能工作?是什么让Python对待这些情况的不同?

我使用Python3,如果它有任何区别。

+2

与大多数魔,它的目的是做的正是这样,如果我得到这个权利:“同样重要的是要注意,用户定义的函数它们是类实例的**属性**不是**转换为绑定方法;只有当函数是类**的**属性时才会发生这种情况。“用'self.script'声明你正在创建一个实例属性。静态方法'Scriptable.script'仍然存在。可能的解决方法是在前脚本方法中调用注入的'脚本'。 –

回答

13

这里是你如何做到这一点:

import types 
class Scriptable: 
    def __init__(self, script = None): 
     if script is not None: 
      self.script = types.MethodType(script, self) # replace the method 
    def script(self): 
     print("greetings from the default script") 

由于ba__friend在评论中指出的,方法是存储在class对象。当您从实例访问属性时,类对象上的描述符将函数作为绑定方法返回。

当您将函数分配给instance时,不会发生特殊情况,所以您必须亲自包装该函数。

+0

酷!在阅读了ba__friend的评论之后,我发现我可以执行'self.script = lambda:script(self)'来获得想要的结果,但是这对于无参数方法是硬编码的。你的好多了。 –

+0

更好地检查脚本是否可以调用,以确保 – kentwait

6

看看这个:

>>> scriptable = Scriptable() 
>>> scriptable.script 
<bound method Scriptable.script of <__main__.Scriptable instance at 0x01209DA0>> 
>>> scriptable = Scriptable(my_script) 
>>> scriptable.script 
<function my_script at 0x00CF9730> 

声明self.script = script只创建一个类对象的属性,没有任何与它的“魔力”。

语句def script(self):在类定义内部创建一个描述符 - 实际上用self参数管理所有东西的特殊对象。

您可以在提到的数据模型参考中阅读关于Python中描述符的更多信息:implementing-descriptors

另一篇关于Raymond Hettinger的Python描述符的伟大文章: How-To Guide for Descriptors

+0

这也是一个非常简单的步骤,完全按照OP的要求进行:http://irrepupavel.com/documents/python/instancemethod/(G​​oogle上的“创建绑定方法”的最高成就)。 –

+0

还有类似的魔法正在发生,但我不认为'def script(self):'实际上会创建一个描述符。如果我做'Scriptable.script = my_script',那么一切都按预期工作,并且没有创建描述符。它看起来像ba__friend的评论是正确的:魔术只适用于* type *的属性,而不适用于* instance *的属性。 –

+0

好的,我纠正了。玩warvariuc的代码后(http://stackoverflow.com/questions/6478371/assigning-a-function-to-an-object-attribute/6479888#6479888),很明显,函数*是*描述符 - 它们自动拥有'__get__'属性,尽管我没有在任何地方看到它。我曾认为'type .__ getattr__'具有特殊的函数代码,但它看起来像实际上只是将它们视为任何其他描述符。 –

1

我真的不能回答你的问题为什么它的工作原理是,你要问吉多·范罗苏姆,但我可以给你一个可能的解决方法:

class Scriptable: 
    def __init__(self, script = None): 
     self._script = script # replace the method 
    def script(self): 
     if self._script: return self._script(self) 
     return self._defaultscript() 
    def _defaultscript(self): 
     print("greetings from the default script") 
9

感谢Alex马尔泰利的answer这里是另外一个版本:

class Scriptable: 
    def script(self): 
     print(self) 
     print("greetings from the default script") 

def another_script(self): 
    print(self) 
    print("greetings from the another script") 

s = Scriptable() 
s.script() 

# monkey patching: 
s.script = another_script.__get__(s, Scriptable) 
s.script() 
+3

疯狂。我不知道函数自动具有'__get__'属性 - 文档甚至没有提及它们 - 但运行你的例子,我可以看到它们。所以'lambda's。因此,每个函数都可以自动用作描述符...... Python继续令我惊叹。我认为特殊的东西变成了一般。 –