简体   繁体   English

将参数传递给数据类会导致 TypeError

[英]Passing the arguments to dataclass causes TypeError

I've been having an issue with dataclasses, despite using the decorator and passing the arguments to the dataclass, I will get a TypeError stating that the object does not take any arguments.尽管使用了装饰器并将参数传递给数据类,但我一直遇到数据类问题,我会收到一个TypeError说明该对象不接受任何参数。 This seems quite temperamental and doesn't seem to be triggered by code changes, something which is working for a colleague is not working for me (but sometimes does).这似乎很喜怒无常,似乎不是由代码更改触发的,对同事有用的东西对我不起作用(但有时会)。 We're both using Python 3.9.7 and we both code with PyCharm.我们都使用 Python 3.9.7 并且都使用 PyCharm 进行编码。

I made the switch from Windows to Ubuntu in an attempt to stop this issue but after a week or so, it is happening again.我从 Windows 切换到 Ubuntu 试图阻止这个问题,但大约一周后,它又发生了。 Here is my last stack trace error:这是我最后一个堆栈跟踪错误:

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)

It is using the factory / plugin pattern to register shapes from JSON, the most notable lines in factory.py being:它使用工厂/插件模式从 JSON 注册形状,factory.py 中最值得注意的行是:

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

Here is the object which is failing:这是失败的对象:

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

I believe this is an issue arising from changes to the behavior of Protocol starting in Python 3.8.我相信这是从 Python 3.8 开始对Protocol行为的更改引起的问题。 In particular, making a dataclass inherit from a protocol will cause problems.特别是,使数据类从协议继承会导致问题。

The issue is that, with newer versions of Python, the inheritance from Protocol will result in Rectangle being assigned an __init__ method before being passed to dataclass .问题是,对于较新版本的 Python,从Protocol的继承将导致Rectangle在被传递给dataclass之前被分配一个__init__方法。 This is problematic because dataclass will not overwrite an existing method, and so your Rectangle class is never assigned an initializer that has rgb , colour , etc. as parameters.这是有问题的,因为dataclass不会覆盖现有方法,因此您的Rectangle类永远不会分配一个具有rgbcolour等作为参数的初始化程序。

One solution would be to make Rectangle not inherit from Shape .一种解决方案是使Rectangle不继承自Shape Since Shape does not provide any method implementations, inheritance is not needed here.由于Shape不提供任何方法实现,因此这里不需要继承。 (See the mypy docs for more details about structural subtyping.) Alternatively, if you would like to have Rectangle inherit from Shape , you could have Shape not inherit from Protocol . (有关结构子类型的更多详细信息,请参阅mypy 文档。)或者,如果您希望Rectangle继承自Shape ,则可以让Shape不继承自Protocol

It might not be the most relevant answer, but because it happened to me, I thought maybe it would be helpful for others.这可能不是最相关的答案,但因为它发生在我身上,我认为它可能对其他人有帮助。

if you're using the dataclass_json from dataclasses_json , the order in which you're annotating the class with, matters.如果您使用dataclass_json中的dataclasses_json ,则注释类的顺序很重要。 So the order should be first @dataclass_json , and then comes @dataclass .所以顺序应该首先是@dataclass_json ,然后是@dataclass Otherwise, you get this weird error which from the first look, it's completely unrelated.否则,你会得到这个奇怪的错误,乍一看,它是完全不相关的。 I hope it helps.我希望它有所帮助。

UPDATE: I could not reproduce the core of my results in new tests.更新:我无法在新测试中重现我的核心结果。 I do not know what is going on here, below, see UPDATE: for the recent test.我不知道这里发生了什么,请参阅下面的更新:最近的测试。 I used Python 3.8.10 in codium for both tests.我在 codium 中使用 Python 3.8.10 进行这两个测试。 Only the second example still throws at least a similar error.只有第二个示例仍然至少引发类似的错误。


The type error appears in this simple example as well:类型错误也出现在这个简单的例子中:

from dataclasses import dataclass

@dataclass
class Point:
     x: int
     y: int

p = Point(10, 20)

Out:出去:

TypeError: Point() takes no arguments

UPDATE: This runs through now.更新:这贯穿现在。


If you add arguments, the error remains the same:如果添加参数,则错误保持不变:

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

p = Point(10, 20)

Out:出去:

TypeError: Point() takes no arguments

This error is thrown since the attributes are not initialized (not in the constructor).由于未初始化属性(不在构造函数中),因此引发此错误。

UPDATE: This throws now TypeError: __init_subclass__() takes no keyword arguments更新:现在TypeError: __init_subclass__() takes no keyword arguments


Either, you fill the attributes step by step:要么,您逐步填写属性:

p.x = 10
p.y = 20

Or you use the following.或者您使用以下内容。

@dataclass
class Point:
    def __init__(self, x, y):
        x: int
        y: int

p = Point(10, 20)

UPDATE: This throws no error and gives Point() for p.更新:这不会引发错误并为 p 提供Point()


Same effect, but a bit more to write, see Data Classes :效果相同,但要编写更多内容,请参阅Data Classes

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

p = Point(10, 20)

UPDATE: This runs through and gives Point() for p.更新:这贯穿并为 p 提供Point()


I guess you should just try an __init__ function like this:我想你应该试试这样的__init__函数:

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

And then work yourself further through the code.然后通过代码进一步工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM