Python 为 class 定义的包装函数的 `self` 参数

Python's `self` argument for class defined wrapped func

我有一个简化的代码:

class A:
    def foo(*args, **kwargs):
        def foo_sub(*args, **kwargs):
            print(f"foo args: {args}")
        return foo_sub

    def bar(*args, **kwargs):
        print(f"bar args: {args}")


a = A()


class B:
    foo = a.foo(1)
    bar = a.bar


a.foo(2)()
a.bar()

B.foo()
B.bar()

B().foo()
B().bar()

并且有输出:

foo args: ()
bar args: (<__main__.A object at 0x7f9763e38080>,)
foo args: ()
bar args: (<__main__.A object at 0x7f9763e38080>,)
foo args: (<__main__.B object at 0x7f9763e38828>,)
bar args: (<__main__.A object at 0x7f9763e38080>,)

我需要将 foo func 包装在 A class 中,我真的不明白为什么 B().foo()self 作为参数传递?我该如何预防?

更复杂的代码示例:https://codehs.com/sandbox/id/python-3-2uVmcT

我不知道你想在这里用所有这些包装器和东西做什么。您可能需要查看装饰器或上下文管理器来干净地执行此操作。

回答你的问题,一种方法是定义静态方法,@MisterMiyagi 也已经指出了这一点。通常在编程中,静态方法是 class 的方法,您可以在不需要 class instance/object 的情况下调用它们。这意味着此类方法没有引用 class 实例的 s/object 状态(以 class attributes/fields 的形式)。在 Python 中,普通 class 方法可以通过这个自动 self 参数访问 instance/object 本身。如果您不想要它,请将其定义为 @staticmethod

A static method does not receive an implicit first argument.

If you access a method (a function defined in a class namespace) through an instance, you get a special object: a bound method (also called instance method) object. When called, it will add the self argument to the argument list.

解决您的问题的快速代码(请注意,正如我之前指出的,有更好的替代方法):

class A:
    @staticmethod  # Or use the inline-decorator stlye as @kaya3 mentioned
    def foo(*args, **kwargs):
        def foo_sub(*args, **kwargs):
            print(f"foo args: {args}")
        return foo_sub

    @staticmethod
    def bar(*args, **kwargs):
        print(f"bar args: {args}")


a = A()


class B:
    @staticmethod
    def foo(*args, **kwargs):
        return a.foo(1)(*args, **kwargs) 

    @staticmethod
    def bar(*args, **kwargs):
        return a.bar(*args, **kwargs)


a.foo(2)()
a.bar()

B.foo()
B.bar()

B().foo()
B().bar()

输出:

foo args: ()
bar args: ()
foo args: ()
bar args: ()
foo args: ()
bar args: ()

否则,如果您真的非常想完全跳过 self 属性并一路破解,您可以触发 obj.method.__func__(*args, **kwargs),如 https://docs.python.org/3/library/stdtypes.html#methods

中所述
  • 警告:这个建议是骇人听闻的。谨慎行事:)

感谢@kaya3 staticmethod 以及所有回复的人!

我找到了元类的解决方案

class BMeta(type):
    def __new__(cls, name, bases, dct):
        if isinstance(dct.get("foo"), FunctionType):
            dct["foo"] = staticmethod(dct["foo"])
        return super().__new__(cls, name, bases, dct)


class B(metaclass=BMeta):
    foo = a.foo(1)
    bar = a.bar

https://codehs.com/sandbox/id/python-3-BaZXiA