在 Python 3 中将方法分配给 var 时,自动柯里化 self 是如何工作的?

发布于 2024-12-18 23:23:01 字数 1340 浏览 4 评论 0原文

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

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

这是一个说明这一点的例子:

import functools

class Wrapper:
    def wrap(self):
        return self._wrapped   #functools.partial(self._wrapped, self)

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

wrapped = Wrapper().wrap()
wrapped('expect self here', 'but', 'get', 'all', 'output')

输出:

WRAPPED! expect self here but get all output
..knows about self: <__main__.Wrapper object at 0x2aaaab2d9f50>

当然,对于普通函数(在类之外),这种魔法不会发生。我什至可以直接在上面的示例中分配该方法,而无需经过实例化:

wrapped = Wrapper._wrapped
wrapped('expect self here', 'but', 'get', 'all', 'output')

现在我得到了我最初期望的结果:

WRAPPED! but get all output
..knows about self: expect self here

在我的原始代码中,我使用了 functools.partial 来柯里化 self,但后来发现这甚至不是必需的。

我喜欢当前的行为,但我尚未理解一致性和“显而易见”的推理。

我在这里使用 Python 3.1.2。

这个问题与使用types.MethodType的答案相关吗?在此处和网络中进行搜索主要会获得有关柯里化/部分函数调用以及参数列表打包/解包的基本信息。也许我使用了不适当的搜索术语(例如“python currying 方法”。)

任何人都可以阐明这种行为吗?

Py2 和 Py3 中的情况相同吗?

I am writing a context manager to wrap the builtins.print function. And this works fine. However I encountered a Python behaviour that I can't wrap my head around:

Whenever a classes' method is assigned into a variable for later calling, the first "self" argument seems to be automatically stored as well and used for all later calls.

Here's an example illustrating the point:

import functools

class Wrapper:
    def wrap(self):
        return self._wrapped   #functools.partial(self._wrapped, self)

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

wrapped = Wrapper().wrap()
wrapped('expect self here', 'but', 'get', 'all', 'output')

The output:

WRAPPED! expect self here but get all output
..knows about self: <__main__.Wrapper object at 0x2aaaab2d9f50>

Of course for normal functions (outside of classes) this magic does not happen. I can even assign that method in the example above directly without going through instantiation:

wrapped = Wrapper._wrapped
wrapped('expect self here', 'but', 'get', 'all', 'output')

And now I get what I first expected:

WRAPPED! but get all output
..knows about self: expect self here

In my original code, I used the functools.partial to curry-in the self, but then discovered that this is not even required.

I like the current behaviour, but I'm not yet understanding the reasoning with respect to consistency and "being obvious".

I'm working with Python 3.1.2 here.

Is this question with the answer to use types.MethodType related? Searching here and in the 'net largely results in basic info on currying/partial function calls and packing/unpacking of arg lists. Maybe I used inadequate search terms (e.g. "python currying methods".)

Can anyone shed some light into this behaviour?

Is this the same in Py2 and Py3?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

萤火眠眠 2024-12-25 23:23:01

每当您从实例中获取该方法(如return self._wrapped)时,self就会被记住。

每当您从类中获取方法(如 Wrapper._wrapped 中)时,self 就不会(无法)被记住。

举个例子,试试这个:

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

upper = str.upper
print(upper())

你会看到 HELLO,然后是 TypeError:描述符 'upper' of 'str' 对象需要一个参数

Whenever you take the method from an instance (as in return self._wrapped) then self will be remembered.

Whenever you take the method from a class (as in Wrapper._wrapped) then self is not (cannot be) remembered.

As an example, try this:

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

upper = str.upper
print(upper())

You'll see HELLO, followed by TypeError: descriptor 'upper' of 'str' object needs an argument

南城追梦 2024-12-25 23:23:01

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

当你这样做时,

return self._wrapped

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

这种情况的“存储”很简单,实例方法知道它们属于哪个实例。如果您不希望出现这种行为,请返回未绑定的类方法。

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

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

When an instance method is called, that call will automatically pass in the instance as the first parameter. This is what happens here.

When you do

return self._wrapped

You will return an instance method. Calling it will pass in the instance as the first parameter, that is self. But in the second case you call the method on the class, and hence there exists no instance to get passed in, so no instance gets passed in.

The "storage" of this is simply that instance methods know which instance they belong to. If you don't want that behavior return the unbound class method instead.

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

    def _wrapped(self, *args, **kwargs):
        print('WRAPPED!', *args, **kwargs)
        print('..knows about self:', self)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文