Skip to content

Document dict behavior when setting equal but not identical key #91081

Description

@malthe
mannequin
BPO 46925
Nosy @rhettinger, @methane, @malthe, @JelleZijlstra, @potiuk
PRs
  • Replace key if not identical to old key in dict #31685
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = None
    created_at = <Date 2022-03-04.23:25:53.644>
    labels = ['3.11', 'type-bug', '3.9', '3.10', 'docs']
    title = 'Document dict behavior when setting equal but not identical key'
    updated_at = <Date 2022-03-07.10:13:25.530>
    user = 'https://github.com/malthe'

    bugs.python.org fields:

    activity = <Date 2022-03-07.10:13:25.530>
    actor = 'potiuk'
    assignee = 'docs@python'
    closed = False
    closed_date = None
    closer = None
    components = ['Documentation']
    creation = <Date 2022-03-04.23:25:53.644>
    creator = 'malthe'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 46925
    keywords = []
    message_count = 9.0
    messages = ['414554', '414560', '414562', '414572', '414581', '414586', '414607', '414636', '414652']
    nosy_count = 6.0
    nosy_names = ['rhettinger', 'methane', 'docs@python', 'malthe', 'JelleZijlstra', 'potiuk']
    pr_nums = ['31685']
    priority = 'normal'
    resolution = None
    stage = 'needs patch'
    status = 'open'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue46925'
    versions = ['Python 3.9', 'Python 3.10', 'Python 3.11']

    Linked PRs

    Activity

    1. malthe commented on Mar 4, 2022

      malthemannequin
      MannequinAuthor

      When a key that is equal to an existing key (but not the same object identity) is used to set a new value, the key itself is not replaced.

      This manifests perhaps most clearly in weakref.WeakKeyDictionary where keys can mysteriously disappear.

      Consider two equal keys, k1 and k2:

      d = WeakKeyDictionary()
      d[k1] = 1
      d[k2] = 2
      del k1

      We would expect the dictionary to have a single entry k2 => 2. But in fact it is empty now.

    2. added
      stdlibStandard Library Python modules in the Lib/ directory
      type-bugAn unexpected behavior, bug, or error
      on Mar 4, 2022
    3. JelleZijlstra commented on Mar 5, 2022

      @JelleZijlstra
      Member

      As @methane also said on the PR, this is a backward compatibility break and we can't just change the behavior. I'd be opposed to a change here: the proposed behavior isn't clearly better than the existing behavior (sometimes you want one behavior, sometimes another), and it will be difficult to find and adjust code that relies on the existing behavior.

    4. rhettinger commented on Mar 5, 2022

      @rhettinger
      Contributor

      I concur with Jelle and Methane that we can't do this without breaking code.

      Also if you don't care about dict order, the work around is easy. Just remove the old key:

      d.pop(k); d[k] = v
      
    5. potiuk commented on Mar 5, 2022

      potiukmannequin
      Mannequin

      How about if we make PR instead to the documentation about this behaviour?

      I think this behaviour is a little surprising (you need to know the internals of how WeakKeyDict keys are constructed and checked) and while I understand you do not want breaking change, maybe this should be explained to the users and example given how to apply the workaround?

      Just following the "least surprise" principle.

    6. JelleZijlstra commented on Mar 5, 2022

      @JelleZijlstra
      Member

      Agree, I couldn't find a place where this behavior is documented. The second paragraph in https://docs.python.org/3.10/library/stdtypes.html#mapping-types-dict comes close. Reopening as a documentation issue.

    7. 10 remaining items

    8. changed the title [-]Replace key if not identical to old key in dict[/-] [+]Document dict behavior when setting equal but not identical key[/+] on Mar 5, 2022
    9. rhettinger commented on Mar 5, 2022

      @rhettinger
      Contributor

      The weakref docs in particular should point out the OP's example and highlight the workaround.

    10. malthe commented on Mar 6, 2022

      malthemannequin
      MannequinAuthor

      Java's HashMap has also the (current) Python behavior. An existing same key is not replaced.

    11. methane commented on Mar 7, 2022

      @methane
      Member

      I don't know much about Java, but Java's WeakHashMap is same to Python's WeakKeyDictionary.

      https://docs.oracle.com/javase/9/docs/api/java/util/WeakHashMap.html

      """
      This class is intended primarily for use with key objects whose equals methods test for object identity using the == operator. Once such a key is discarded it can never be recreated, so it is impossible to do a lookup of that key in a WeakHashMap at some later time and be surprised that its entry has been removed. This class will work perfectly well with key objects whose equals methods are not based upon object identity, such as String instances. With such recreatable key objects, however, the automatic removal of WeakHashMap entries whose keys have been discarded may prove to be confusing.
      """

    12. potiuk commented on Mar 7, 2022

      potiukmannequin
      Mannequin

      Yeah. Sounds like adding docs is indeed needed :)

    13. transferred this issue fromon Apr 10, 2022
    14. added a commit that references this issue on Dec 21, 2022
    15. added 4 commits that reference this issue on Dec 21, 2022
    16. hauntsaninja commented on Dec 24, 2022

      @hauntsaninja
      Contributor

      Thanks to slateny, the behaviour is now documented in the weakref docs. Since this addresses OP's issue, which is where this is most clearly pernicious, I'm closing. But do let me know if you think this should be documented more generally somewhere else.

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

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      3.10 (EOL)end of life3.11only security fixes3.9 (EOL)end of lifedocsDocumentation in the Doc dirtype-bugAn unexpected behavior, bug, or error

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions