【问题标题】:Assigning a function to an object attribute将函数分配给对象属性
【发布时间】:2011-09-22 15:08:20
【问题描述】:

根据我对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。

【问题讨论】:

  • 就像大多数魔法一样,如果我做对了,它的设计就是这样做的:“同样重要的是要注意用户定义的函数是 类实例的属性 转换为绑定方法;仅当函数是类的属性时才会发生这种情况。"使用self.script 语句,您正在创建一个实例属性。静态方法Scriptable.script 仍然存在。一种可能的解决方法是在以前的 script 方法中调用您注入的 script

标签: python methods


【解决方案1】:

这是你的做法:

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 在 cmets 中指出的那样,方法存储在 class 对象上。当您从实例访问属性时,类对象上的描述符将函数作为绑定方法返回。

当您将函数分配给 instance 时,不会发生任何特殊情况,因此您必须自己包装函数。

【讨论】:

  • 酷!在阅读了 ba__friend 的评论后,我发现我可以通过 self.script = lambda: script(self) 来获得想要的结果,但这对于无参数方法是硬编码的。你的好多了。
  • 最好检查 script 是否可以调用以确保
【解决方案2】:

感谢 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()

【讨论】:

  • 疯了。我不知道函数自动具有__get__ 属性——文档甚至没有提到它们有——但是运行你的例子,我可以看到它们有。 lambdas 也是如此。所以每个函数都可以自动用作描述符...... Python 继续让我惊叹。我认为是特殊情况的事情结果是普遍的。
【解决方案3】:

看看这个:

>>> 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.

【讨论】:

  • 这也是一个非常简单的演练,完全符合 OP 的要求:irrepupavel.com/documents/python/instancemethod(“python 创建绑定方法”在 Google 上的热门话题)。
  • 还有类似的魔法,但我不认为def script(self): 实际上创建了一个描述符。如果我这样做Scriptable.script = my_script,那么一切都会按预期工作,并且不会在那里创建描述符。看起来 ba__friend 的评论是正确的:魔法只适用于 type 上的属性,而不适用于 instance 上的属性。
  • 好的,我的立场是正确的。在使用了warvariuc 的代码(stackoverflow.com/questions/6478371/…) 之后,很明显函数 描述符——它们自动具有__get__ 属性,尽管我在任何地方都没有看到这一点。我原以为type.__getattr__ 有函数的特殊代码,但看起来它实际上只是像对待任何其他描述符一样对待它们。
【解决方案4】:

我无法真正回答你的问题为什么会这样,你必须问 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")

【讨论】:

    猜你喜欢
    • 2016-01-15
    • 1970-01-01
    • 2014-04-05
    • 2014-01-16
    • 2016-02-03
    • 2020-05-22
    • 1970-01-01
    • 2016-03-09
    相关资源
    最近更新 更多