【问题标题】:Dataclass and Callable Initialization Problem via Classmethods通过类方法的数据类和可调用初始化问题
【发布时间】:2022-01-06 19:41:49
【问题描述】:

我发现了这种奇怪的行为,我不知道我是否是问题所在,或者这是否是 python / dataclass / callable 错误。

这是一个最小的工作示例

from dataclasses import dataclass
from typing import Callable
import numpy as np


def my_dummy_callable(my_array, my_bool):
    return 1.0


@dataclass()
class MyDataClassDummy:

    my_data: int = 1
    my_callable: Callable[[np.ndarray, bool], float] = my_dummy_callable

    def __init__(self):
        print("I initialized my Class!")

    @classmethod
    def my_factory_with_callable_setting(cls):
        my_dummy = MyDataClassDummy()
        my_dummy.my_callable = my_dummy_callable
        return my_dummy

    @classmethod
    def my_factory_without_callable_setting(cls):
        my_dummy = MyDataClassDummy()
        return my_dummy

    def do_something(self):
        print("This is my data", self.my_data)
        print("This is the name of my callable", str(self.my_callable))
        return self.my_callable(np.empty(shape=(42, 42)), True) + self.my_data


@dataclass()
class MySecondDataClassDummy:
    my_data: int = 4
    my_callable: Callable[[np.ndarray, bool], float] = my_dummy_callable

    @classmethod
    def my_factory(cls):
        my_dummy = MySecondDataClassDummy()
        return my_dummy

    def do_something(self):
        print("This is my data", self.my_data)
        print("This is the name of my callable", str(self.my_callable))
        return self.my_callable(np.empty(shape=(42, 42)), True) - self.my_data


if __name__ == '__main__':
    # this works
    my_first_dummy = MyDataClassDummy.my_factory_with_callable_setting()
    my_first_dummy.do_something()

    # this also works
    my_second_dummy = MySecondDataClassDummy.my_factory()
    my_second_dummy.do_something()

    # this does not work
    my_other_dummy = MyDataClassDummy.my_factory_without_callable_setting()
    my_other_dummy.do_something()

case1:用工厂初始化,用我自己的 init 初始化,然后在初始化后显式设置可调用对象(尽管有默认值) - 有效

case2:用工厂初始化,但自己不明确编码 init() - 有效

case3:用工厂初始化,用我自己的 init 初始化,并且在初始化后不显式设置可调用对象(因为这就是我有默认值的原因,不是吗?!) - 不起作用但会抛出错误:

Traceback (most recent call last):
  File "my_path/dataclass_dummy.py", line 63, in <module>
    my_other_dummy.do_something()
  File "my_path/dataclass_dummy.py", line 33, in do_something
    return self.my_callable(np.empty(shape=(42, 42)), True) + self.my_data
TypeError: my_dummy_callable() takes 2 positional arguments but 3 were given

所以现在我想知道,在第三种情况下我做错了什么。

我正在使用 Python 3.8 和 numpy 1.20.2

【问题讨论】:

    标签: python python-3.x numpy callable python-dataclasses


    【解决方案1】:

    @dataclass 装饰器默认为类提供__init__() 方法。此方法将类型注释的类变量转换为类实例的属性。在类MySecondDataClassDummy 的情况下使用此机制。实际上,这个类的每个实例都有一个属性my_callable。由于该属性是一个函数,因此您可以像在案例 2 中那样调用它,并且一切正常。

    MyDataClassDummy 类有自己的__init__() 方法,它覆盖了@dataclass 提供的__init__()。然后这个类的实例或多或少地被初始化,就像没有@dataclass 装饰器一样。特别是,作为函数的类变量成为类实例的绑定方法。结果my_callable就变成了这样一个绑定的方法,当你在Case 3中执行时

    self.my_callable(np.empty(shape=(42, 42)), True)  
    

    然后self 用作my_callable 的第一个参数。由于此函数只接受两个参数,因此会产生错误。

    在情况 1 中不会出现相同的问题,因为在这种情况下您修改 my_dummy.my_callable 使其成为 my_dummy 的属性,其值为函数。修改后,不再是绑定方法了。

    【讨论】:

    • 这是有道理的,而且是我认为我会想念的东西,谢谢!因此,如果我在 MyDataClassDummy 中实现 __post_init__ 函数而不是 __init__ 函数来添加我自己的初始化代码,那么这三种情况都有效。 (我找到了__post_init__here
    猜你喜欢
    • 1970-01-01
    • 2020-10-17
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-20
    • 2022-10-15
    • 1970-01-01
    相关资源
    最近更新 更多