根据我对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。
答案 0 :(得分:15)
以下是您的操作方法:
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
分配一个函数时,没有任何特殊情况发生,所以你必须自己包装这个函数。
答案 1 :(得分:9)
感谢Alex Martelli的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()
答案 2 :(得分: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
答案 3 :(得分:1)
我无法真正回答你的问题为什么它是这样的,你必须问Guido van Rossum,但我可以给你一个可能的解决方法:
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")