【问题标题】:Passing the arguments to dataclass causes TypeError将参数传递给数据类会导致 TypeError
【发布时间】:2021-12-25 23:35:46
【问题描述】:

我一直遇到数据类的问题,尽管使用了装饰器并将参数传递给数据类,但我会得到一个TypeError,说明该对象不接受任何参数。这似乎很喜怒无常,似乎不是由代码更改触发的,对同事有用的东西对我不起作用(但有时会)。我们都使用 Python 3.9.7,并且都使用 PyCharm 编写代码。

我从 Windows 切换到 Ubuntu 试图阻止这个问题,但大约一周后,它再次发生。这是我最后一个堆栈跟踪错误:

Traceback (most recent call last):
  File "/home/pedro/Documents/datacollect/Python-Shape-Game/main.py", line 188, in <module>
    shapes = load_shapes()
  File "/home/pedro/Documents/datacollect/Python-Shape-Game/main.py", line 139, in load_shapes
    return [factory.create(item) for item in data["shapes"]]
  File "/home/pedro/Documents/datacollect/Python-Shape-Game/main.py", line 139, in <listcomp>
    return [factory.create(item) for item in data["shapes"]]
  File "/home/pedro/Documents/datacollect/Python-Shape-Game/factory.py", line 28, in create
    return creation_function(**arguments)
TypeError: Rectangle() takes no arguments
pygame 2.1.0 (SDL 2.0.16, Python 3.9.7)

它使用工厂/插件模式从 JSON 中注册形状,factory.py 中最值得注意的行是:

shape_creation_functions: dict[str, Callable[..., Shape]] = {}
# ...
creation_function = shape_creation_functions[shape_type]
return creation_function(**arguments)

这是失败的对象:

class Shape(Protocol):
    """Represents a shape"""
    type: str
    rgb: list
    colour: tuple
    method: str
    positions: dict
    radius: int

    def map(self, position: str) -> Union[list[tuple[Any, Any]], tuple]:
        """Map the shape to the screen"""

@dataclass()
class Rectangle(Shape):
    """Represents a rectangle"""
    type: str
    rgb: list
    colour: tuple
    method: str
    positions: dict

def map(self, position: str) -> Union[list[tuple[Any, Any]], tuple]:
    """Draw the shape on the screen"""

    rect = (
        self.positions[position][0],
        self.positions[position][1],
        self.positions[position][2],
        self.positions[position][3]
    )

    return rect

【问题讨论】:

  • 请分享Shape 代码和试图创建Rectangle 的代码
  • 一般来说 - 你在这里尝试做什么?从 dict 创建一个数据类?
  • 请将代码添加到帖子中 - 不要作为评论。
  • 在我们继续之前 - 你能解释一下当前的继承吗?为什么我们在基类和矩形中都找到相同的属性?
  • @HelloSpeakman:是的,我现在明白了。谢谢,抱歉打扰了。

标签: python ubuntu-20.04 python-3.9


【解决方案1】:

我认为这是从 Python 3.8 开始对 Protocol 的行为进行更改而引起的问题。特别是,让数据类从协议继承会导致问题。

问题在于,对于较新版本的 Python,从 Protocol 的继承将导致 Rectangle 在传递给 dataclass 之前被分配一个 __init__ 方法。这是有问题的,因为dataclass 不会覆盖现有方法,因此您的Rectangle 类永远不会分配一个以rgb、colour 等为参数的初始化程序。

一种解决方案是让Rectangle 不继承自Shape。由于Shape 没有提供任何方法实现,所以这里不需要继承。 (有关结构子类型的更多详细信息,请参阅mypy docs。)或者,如果您想让Rectangle 继承自Shape,则可以让Shape 不继承自Protocol。

【讨论】:

    【解决方案2】:

    更新:我无法在新测试中重现我的核心结果。我不知道这里发生了什么,请参阅 UPDATE: 了解最近的测试。我在 codium 中使用 Python 3.8.10 进行这两个测试。 只有第二个示例仍然引发至少类似的错误。


    这个简单的例子中也出现了类型错误:

    from dataclasses import dataclass
    
    @dataclass
    class Point:
         x: int
         y: int
    
    p = Point(10, 20)
    

    输出:

    TypeError: Point() takes no arguments
    

    更新:到现在为止。


    如果添加参数,错误依旧:

    @dataclass
    class Point(x=0,y=0):
         x: int
         y: int
    
    p = Point(10, 20)
    

    输出:

    TypeError: Point() takes no arguments
    

    由于属性未初始化(不在构造函数中)而引发此错误。

    更新:现在抛出 TypeError: __init_subclass__() takes no keyword arguments


    要么,你一步一步填写属性:

    p.x = 10
    p.y = 20
    

    或者你使用下面的。

    @dataclass
    class Point:
        def __init__(self, x, y):
            x: int
            y: int
    
    p = Point(10, 20)
    

    更新:这不会引发错误并为 p 提供 Point()。


    同样的效果,但要多写一点,见Data Classes:

    @dataclass
    class Point:
        def __init__(self, x:int, y:int):
            self.x = x
            self.y = y
    
    p = Point(10, 20)
    

    更新:这贯穿并为 p 提供Point()。


    我想你应该试试这样的__init__ 函数:

    class Rectangle():
        def __init__(self, Shape):
            Shape: object
    

    然后自己进一步完成代码。

    【讨论】:

    • 这个例子在我使用 Python 3.9.7 时没有导致错误。
    • @MaxRadin 很好,您测试了它。那可能是我这边的配置问题。我不认为稍微不同的 Python 版本会有所不同。我把它留作文档。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-25
    • 1970-01-01
    • 1970-01-01
    • 2013-01-10
    • 1970-01-01
    • 2015-09-08
    • 1970-01-01
    相关资源
    最近更新 更多