MartinThoma / flake8-simplify

❄ A flake8 plugin that helps you to simplify code
MIT License
186 stars 19 forks source link

[SIM119] Detect potential dataclasses #37

Closed MartinThoma closed 3 years ago

MartinThoma commented 3 years ago

Explanation

Dataclasses were introduced with PEP 557. Data Classes can be thought of as "mutable namedtuples with defaults".

Dataclasses should be used when the primary focus is to store data.

Dataclasses help, because they have a good __str__ and __eq__ implementation and you don't have to write __init__.

Example

# Bad
class Person:
    def __init__(self, first_name, last_name, birthdate):
        self.first_name = first_name
        self.last_name = last_name
        self.birthdate = birthdate

# Good
from dataclasses import dataclass
from datetime import date

@dataclass
class Person:
    first_name: str
    last_name: str
    birthdate: date

As an example why dataclasses are nice:

p11 = Person1("Bob", "Builder", date(2000, 1, 3))
p12 = Person1("Bob", "Builder", date(2000, 1, 3))
p21 = Person2("Bob", "Builder", date(2000, 1, 3))
p22 = Person2("Bob", "Builder", date(2000, 1, 3))

print(p11)
print(p11 == p12)
print("-" * 80)
print(p21)
print(p21 == p22)

gives:

<__main__.Person1 object at 0x7f3f8a32aac0>
False
--------------------------------------------------------------------------------
Person2(first_name='Bob', last_name='Builder', birthdate=datetime.date(2000, 1, 3))
True
MartinThoma commented 3 years ago
$ astpretty --no-show-offsets /dev/stdin <<< `cat example.txt`
    [...]
        ClassDef(
            name='Person',
            bases=[],
            keywords=[],
            body=[
                FunctionDef(
                    name='__init__',
                    args=arguments(
                        posonlyargs=[],
                        args=[
                            arg(arg='self', annotation=None, type_comment=None),
                            arg(arg='first_name', annotation=None, type_comment=None),
                            arg(arg='last_name', annotation=None, type_comment=None),
                            arg(arg='birthdate', annotation=None, type_comment=None),
                        ],
                        vararg=None,
                        kwonlyargs=[],
                        kw_defaults=[],
                        kwarg=None,
                        defaults=[],
                    ),
                    body=[
                        Assign(
                            targets=[
                                Attribute(
                                    value=Name(id='self', ctx=Load()),
                                    attr='first_name',
                                    ctx=Store(),
                                ),
                            ],
                            value=Name(id='first_name', ctx=Load()),
                            type_comment=None,
                        ),
                        Assign(
                            targets=[
                                Attribute(
                                    value=Name(id='self', ctx=Load()),
                                    attr='last_name',
                                    ctx=Store(),
                                ),
                            ],
                            value=Name(id='last_name', ctx=Load()),
                            type_comment=None,
                        ),
                        Assign(
                            targets=[
                                Attribute(
                                    value=Name(id='self', ctx=Load()),
                                    attr='birthdate',
                                    ctx=Store(),
                                ),
                            ],
                            value=Name(id='birthdate', ctx=Load()),
                            type_comment=None,
                        ),
                    ],
                    decorator_list=[],
                    returns=None,
                    type_comment=None,
                ),
            ],
            decorator_list=[],
        )
MartinThoma commented 3 years ago

And the dataclass:

$ astpretty --no-show-offsets /dev/stdin <<< `cat example.txt`
        ClassDef(
            name='Person',
            bases=[],
            keywords=[],
            body=[
                AnnAssign(
                    target=Name(id='first_name', ctx=Store()),
                    annotation=Name(id='str', ctx=Load()),
                    value=None,
                    simple=1,
                ),
                AnnAssign(
                    target=Name(id='last_name', ctx=Store()),
                    annotation=Name(id='str', ctx=Load()),
                    value=None,
                    simple=1,
                ),
                AnnAssign(
                    target=Name(id='birthdate', ctx=Store()),
                    annotation=Name(id='date', ctx=Load()),
                    value=None,
                    simple=1,
                ),
            ],
            decorator_list=[Name(id='dataclass', ctx=Load())],
        )