-
-
Notifications
You must be signed in to change notification settings - Fork 34.3k
Open
Labels
pendingThe issue will be closed if no feedback is providedThe issue will be closed if no feedback is providedstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Description
Bug report
Bug description:
Just found a weird behavior with objects interned via caching __new__ result: __new__ is called with no additional arguments during copying which leads to unexpected behavior (all copies become the same object)
import copy
from functools import cache
class Dummy:
def __init__(self, field):
self.field = field
@cache
def __new__(cls, *args, **kwargs):
print(f'__new__ is called with {args=} {kwargs=}')
return super(Dummy, cls).__new__(cls)
a, b, c = Dummy('a'), Dummy('b'), Dummy('c')
# __new__ is called with args=('a',) kwargs={}
# __new__ is called with args=('b',) kwargs={}
# __new__ is called with args=('c',) kwargs={}
print(vars(a), vars(b), vars(c))
# {'field': 'a'} {'field': 'b'} {'field': 'c'}
dc_a = copy.deepcopy(a)
# __new__ is called with args=() kwargs={}
print(vars(dc_a))
# {'field': 'a'}
dc_b = copy.deepcopy(b)
print(vars(dc_a), vars(dc_b))
# {'field': 'b'} {'field': 'b'}
dc_a is dc_b
# True
dc_c = copy.deepcopy(c)
print(vars(dc_a), vars(dc_b), vars(dc_c))
# {'field': 'c'} {'field': 'c'} {'field': 'c'}
dc_c is dc_b
# True
# with copy.copy works too
c_a = copy.copy(a)
c_b = copy.copy(b)
c_c = copy.copy(c)
print(vars(c_a), vars(c_b), vars(c_c))
# {'field': 'c'} {'field': 'c'} {'field': 'c'}
c_a is dc_a
# True
c_c is c
# FalseIt works with frozen sloted dataclasses as well
@dataclass(slots=True, frozen=True)
class Dummy:
str_field: str
int_field: int
@cache
def __new__(cls, *args, **kwargs):
print(f'__new__ called with {args=} {kwargs=}')
return super(Dummy, cls).__new__(cls)
Dummy('a', 1) is Dummy('a', 1)
# __new__ called with args=('a', 1) kwargs={}
# True
Dummy('a', 1) is not Dummy('b', 1)
# __new__ called with args=('b', 1) kwargs={}
# True
Dummy('a', 1) is copy.deepcopy(Dummy('a', 1))
# __new__ called with args=() kwargs={}
# False
copy.deepcopy(Dummy('a', 1)) is copy.deepcopy(Dummy('b', 1))
# True
Yes, there is a workaround requiring to define something like this in the class
def __deepcopy__(self, memo=None):
return self
But it still looks like a bug.
If it is somehow an intended behavior it would be useful to point that out here: https://docs.python.org/3/library/copy.html
CPython versions tested on:
3.14
Operating systems tested on:
macOS
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
pendingThe issue will be closed if no feedback is providedThe issue will be closed if no feedback is providedstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Projects
Status
No status