使用 __setattr__ 重写库的整个方法 class 问题:缺少 1 个必需的位置参数:'self'

Usage of __setattr__ to rewrite whole method of library class issue: missing 1 required positional argument: 'self'

我有一些结构复杂的导入包 并且需要调用一些基于许多其他方法的方法 具有非默认参数,这些参数本身不像 sklearn 中的管道那样 class 属性。

此模块结构的最小示例:

class Library_class:
    def __init__(
        self,
        defined_class_options,
      ):
        self.defined_class_options = defined_class_options
        
    def method1( self , default_non_class_arg = 12 ):
        
        assert self.defined_class_options==3
        return default_non_class_arg
        
    def method2( self, image ):
        return image/ self.method1()

默认用法:

    class_instance = Library_class( 3 )
    class_instance.method2( 36 )
> 3.0

例如我需要将default_non_class_arg设置为6。

我尝试了多种方法:

  1. 类似于
    class_instance.method2( 36 ,
                 method1__default_non_class_arg=3  )

TypeError: method2() got an unexpected keyword argument 'method1__default_non_class_arg'

它不起作用可能是因为 class 绝对没有 set_params

  1. 使用 setattr 重新定义函数
    class_instance.__setattr__('method1',Library_class.new_method1)
    class_instance.method2( 36 )

TypeError: new_method1() missing 1 required positional argument: 'self'

你的片段和问题都非常混乱,几乎无法阅读。

无论如何,如果您想用另一个函数替换 method1,例如在特定实例中说 new_method1,就这样做。您对 .__setattr__ 的调用可以做到这一点,但根本不需要它(如果需要,因为您没有在编写代码时替换名称的方法,并且需要它作为参数,它是调用 built-in setattr 而不是实例方法更正确:`setattr(class_instance, "method1", new_method1").

通常,如果您知道,在编写代码时必须替换实例中的“method1”,赋值运算符会执行此操作:

class_instance.method1 = new_method1

你的例子中出了什么问题是,如果你将一个方法分配给一个实例,而不是 class,你就绕过了 Python 用来插入 [=18= 的机制] 属性 - 所以你的 new_method1 需要一个不同的签名。 (这正是错误消息“TypeError: new_method1() missing 1 required positional argument: 'self'”的意思):

class  MyClass:
    ...
    def method1(self, param1=36):
         ...
    ...

def new_method1(param1=6):   # <-- written outside of any class body, sans self
    ...

my_instance = MyClass()
my_instance.method1 = new_method1 

这会奏效。 new_method1 也可以写在 class 主体中,并且可以同样替换,但是你必须在没有 self 参数的情况下编写它,然后就不会了像正常方法一样直接工作。

或者,您可以在赋值时自己插入 self 参数 - functools.partial 调用是一种方便的方法:

class 我的班级: ... def method1(self, param1=36): ...

def new_method1(self, param1=6):  
     ...
...

my_instance = MyClass()

从 functools 导入部分 MyClass.method1 = partial(MyClass.new_method1, my_instance)


现在,这应该可以回答您的问题,但我不诚实地结束回答而不说这不是一个好的设计是不诚实的。最好的办法是从另一个地方提取参数,它可能来自实例属性,而不是完全替换方法只是为了更改它。

因为对于普通属性,如果没有实例属性存在,Python将读取class属性,它会自然发生,你所要做的就是在你的文件中设置新的默认值实例.

class  MyClass:
    default_param_1 = 36  # Class attribute. Valid for every instance unless overriden
    ...
    def method1(self, param1=None):
         if param1 is None:
               param1 = self.default_param_1  #Automatically fetched from the class if not set on the instance
         ...
    ...


my_instance = MyClass()
my_instance.default_param_1 = 6
...