简体   繁体   中英

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. 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.

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. 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:

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. 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 . 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.

One solution would be to make Rectangle not inherit from Shape . Since Shape does not provide any method implementations, inheritance is not needed here. (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 .

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. So the order should be first @dataclass_json , and then comes @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. 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


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.


Same effect, but a bit more to write, see 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.


I guess you should just try an __init__ function like this:

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

And then work yourself further through the code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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