Methods 在Python3中,将一个方法分配到一个var中时,自动套用self是如何工作的?

Methods 在Python3中,将一个方法分配到一个var中时,自动套用self是如何工作的?,methods,python-3.x,function-pointers,currying,Methods,Python 3.x,Function Pointers,Currying,我正在编写一个上下文管理器来包装builtins.print函数。这个很好用。然而,我遇到了一个我无法理解的Python行为: 每当一个类的方法被分配到一个变量中供以后调用时,第一个“self”参数似乎也会被自动存储并用于以后的所有调用 下面是一个例子来说明这一点: 输出: WRAPPED! expect self here but get all output ..knows about self: <__main__.Wrapper object at 0x2aaaab2d9f50&

我正在编写一个上下文管理器来包装builtins.print函数。这个很好用。然而,我遇到了一个我无法理解的Python行为:

每当一个类的方法被分配到一个变量中供以后调用时,第一个“self”参数似乎也会被自动存储并用于以后的所有调用

下面是一个例子来说明这一点:

输出:

WRAPPED! expect self here but get all output
..knows about self: <__main__.Wrapper object at 0x2aaaab2d9f50>
现在我得到了我最初的期望:

WRAPPED! but get all output
..knows about self: expect self here
在我的原始代码中,我使用了
functools.partial
self
中进行curry,但后来发现这甚至不是必需的

我喜欢目前的行为,但我还不理解关于一致性和“显而易见”的理由

我在这里使用的是Python 3.1.2

与使用
types.MethodType
的答案相关吗?在这里和“网络”中搜索主要会得到有关curry/partial函数调用和arg列表打包/解包的基本信息。可能我使用了不充分的搜索词(例如“python currying methods”。)

有人能解释一下这种行为吗


Py2和Py3中的情况相同吗?

无论何时从实例中获取方法(如
返回self.\u包装的
),都会记住
self


无论何时从类中获取方法(如在Wrapper.\u wrapped中),都不会(无法)记住
self

例如,请尝试以下方法:

upper = 'hello'.upper
print(upper())

upper = str.upper
print(upper())

您将看到
HELLO
,然后是
TypeError:str对象的描述符'upper'需要一个参数

当调用实例方法时,该调用将自动作为第一个参数传入实例。这就是这里发生的事情

当你这样做的时候

return self._wrapped
您将返回一个实例方法。调用它将作为第一个参数传入实例,即self。但是在第二种情况下,您调用类上的方法,因此不存在要传入的实例,因此不传入任何实例

其“存储”简单地说就是实例方法知道它们属于哪个实例。如果不希望该行为,请返回unbound类方法

class Wrapper:
    def wrap(self):
        return Wrapper._wrapped

    def _wrapped(self, *args, **kwargs):
        print('WRAPPED!', *args, **kwargs)
        print('..knows about self:', self)

经过更多的研究,我可以看到绑定方法与未绑定方法之间的关系。看见但是我仍然想知道,在赋值过程中,一个特定的
self
存储到该方法中。如果您想抑制这种行为,可以使用staticmethod:来自Perl,我有同样的问题。(对于一个做了一些接近的事情的Perl模块,请参阅)我认为这是对我在问题中提供的示例的一个很好且有效的简化。谢谢你。您能否指出引入此行为的原因?我可以从两个方面来论证:这是一个很好的特性(我逐渐相信它是),或者这是在行为中引入了一种不明显的不一致性。@cfi:这种行为是至少十年前引入的,而不是二十年前引入的。:-)哎呀,一直都是这样。这种行为是一贯的。@Lennart:请原谅我的无知——我现在只使用Python几个月了。因此,您主张随着时间的推移保持一致性。我的意思是curry函数和方法之间的一致性。但别误会我的意思:我明白你的意思,而且——再一次——我认为这是一个特点(=给我带来积极的惊喜)。如果它存在这么长时间,是否有任何“官方”(Guido、PEP、电子邮件线程?)来解释为什么引入了它?显然这很有用。不记得用其他语言(例如C++,Perl)@ CFI看到这一点:老实说,我认为它一直是这样,所以可能没有文本为什么它是这样实现的。而且这和咖喱没有任何关系。只是当您调用实例方法时,self会自动传入。C之间的区别在于,当您在Python中获得对实例方法的引用时,它将保持一个实例方法,而在C++中,它只是指向内存位置的指针。这是因为C++不是高级语言,而是围绕一种半可移植的低级语言的OO包装器。@莱纳特C++不仅仅是OO包装器。也许在它的概念上,它可以被认为是这样的,但它的发展远远不止于此。谢谢(!)你宝贵的评论和这个答案。如果可能的话,我会在Ethan和你的答案之间打上“答案”复选标记。
return self._wrapped
class Wrapper:
    def wrap(self):
        return Wrapper._wrapped

    def _wrapped(self, *args, **kwargs):
        print('WRAPPED!', *args, **kwargs)
        print('..knows about self:', self)