Skip to content

gh-148941: Skip generating __init__, __repr__, __eq__ when class already defines them#153601

Open
q121212 wants to merge 1 commit into
python:mainfrom
q121212:gh-148941-dataclass-skip-methods
Open

gh-148941: Skip generating __init__, __repr__, __eq__ when class already defines them#153601
q121212 wants to merge 1 commit into
python:mainfrom
q121212:gh-148941-dataclass-skip-methods

Conversation

@q121212

@q121212 q121212 commented Jul 11, 2026

Copy link
Copy Markdown

gh-148941: Skip generating init, repr, eq when class already defines them

Bug

When @dataclass(init=True) (the default) is applied to a class that
already defines __init__ in its own __dict__, the generated
__init__ is always discarded by _set_new_attribute. However,
_init_fn was still called unconditionally, and its field-ordering
validation raised TypeError when inherited fields with defaults were
followed by fields without defaults:

from dataclasses import dataclass

@dataclass
class Base:
    x: int = 0  # has a default

@dataclass
class Child(Base):
    y: int      # no default — would make generated signature invalid
    def __init__(self, y):
        self.y = y
# TypeError: non-default argument 'y' follows default argument 'x'

The docs state: "If the class already defines __init__(), this
parameter is ignored."
The design tables at the top of
Lib/dataclasses.py also document this as the intended behaviour:
for all of __init__, __repr__, and __eq__, the cell where
parameter=True and class has method in __dict__ is intentionally
blank (= no action).

Fix

Add '__x__' not in cls.__dict__ guards to the if init:, if repr:,
and if eq: blocks in _process_class. This skips the generation
(and its validation) entirely when the class's own method would have
been preserved by _set_new_attribute anyway.

Methods with 'raise' cells in the design tables (order, frozen)
are intentionally NOT skipped early — their existing
TypeError-on-overwrite behaviour is correct.

Tests

test_skip_methods_when_defined.py added to Lib/test/test_dataclasses/
covering:

  • __init__: base with default + child without default (the exact
    reproducer from dataclass tries to generate __init__() even though the class already defines one #148941), default_factory variant, KW_ONLY variant,
    three-level inheritance chain
  • __init__: NOT skipped when only a base class defines it (only
    cls.__dict__ matters)
  • __repr__: skipped when custom __repr__ defined
  • __eq__: skipped when custom __eq__ defined
  • order / frozen: still raise TypeError (raise cells untouched)

All existing dataclasses tests continue to pass.

NEWS

Misc/NEWS.d/next/Library/...rst added.

…s already defines them

When @DataClass(init=True) is applied to a class that already defines
__init__ in its own __dict__, the generated __init__ is always discarded
by _set_new_attribute.  However, _init_fn was still called unconditionally,
and its field-ordering validation raised TypeError for inherited fields
with defaults followed by fields without.

Add '__x__' not in cls.__dict__ guards to the if init:, if repr:, and
if eq: blocks in _process_class.  Methods with 'raise' cells in the
design tables (order, frozen) are intentionally NOT skipped early.
@q121212 q121212 requested a review from ericvsmith as a code owner July 11, 2026 23:55
@bedevere-app

bedevere-app Bot commented Jul 11, 2026

Copy link
Copy Markdown

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

@python-cla-bot

python-cla-bot Bot commented Jul 11, 2026

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant