classification
Title: Dict views should be introspectable
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.10
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: Dennis Sweeney, methane, pablogsal, remi.lapeyre, rhettinger, serhiy.storchaka
Priority: normal Keywords: patch

Created on 2020-06-06 17:21 by rhettinger, last changed 2020-06-15 05:55 by serhiy.storchaka. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 20691 closed Dennis Sweeney, 2020-06-07 08:56
PR 20749 merged Dennis Sweeney, 2020-06-09 00:15
PR 20873 closed Dennis Sweeney, 2020-06-14 20:54
PR 20876 merged pablogsal, 2020-06-15 00:41
Messages (19)
msg370841 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2020-06-06 17:21
Dict views wrap an underlying mapping but don't expose that mapping as an attribute.

Traditionally, we do expose wrapped objects:  property() exposes fget,  partial() exposes func, bound methods expose __func__, ChainMap() exposes maps, etc.

Exposing this attribute would help with introspection, making it possible to write efficient functions that operate on dict views.
msg370875 - (view) Author: Dennis Sweeney (Dennis Sweeney) * (Python triager) Date: 2020-06-07 06:20
I'd be happy to write a PR.

Method names could be "mapping", "target", "target_mapping", "target_dict", "referent_dict", etc. 

I like the choice of "target_mapping":

    d = dict()
    assert d is d.keys().target_mapping
    assert d is d.values().target_mapping
    assert d is d.items().target_mapping
msg370876 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2020-06-07 06:28
Just "mapping" would suffice.   That is variable name used in the ABCs, the name defined in the glossary, and the variable name used in ChainMap (in plural form).
msg370877 - (view) Author: Dennis Sweeney (Dennis Sweeney) * (Python triager) Date: 2020-06-07 06:37
I think this will also require typing.MappingProxyType to change a bit, since it would make a proxy's underlying dict accessible:

    >>> d = dict()
    >>> proxy = MappingProxyType(d)
    >>> type(proxy.items()) is type(d.items())  # should be False
    True
    >>> proxy.items().mapping is d  # should be False
    ???
msg370882 - (view) Author: Dennis Sweeney (Dennis Sweeney) * (Python triager) Date: 2020-06-07 09:02
Indeed, with PR 20691 applied, the following crashes:

>>> vars(str).items().mapping.clear()
>>> "uh oh"
msg370884 - (view) Author: Dennis Sweeney (Dennis Sweeney) * (Python triager) Date: 2020-06-07 10:37
Would the best way to address this be adding new KeysProxy, ValuesProxy, and ItemsProxy types?
msg370885 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2020-06-07 10:57
I am not sure that it is good idea to expose internal details (do you want to expose also internal details of iterators?), but if expose an underlying dict of a dict view, I think it should be either a copy or a read-only proxy. It should be enough to help with introspection, but will not open the hole in integrity.
msg370892 - (view) Author: Inada Naoki (methane) * (Python committer) Date: 2020-06-07 12:11
> Would the best way to address this be adding new KeysProxy, ValuesProxy, and ItemsProxy types?

I feel it is too much.  Until real high need is demonstrated, I don't want to introduce such complexity.
msg370893 - (view) Author: RĂ©mi Lapeyre (remi.lapeyre) * Date: 2020-06-07 12:35
property, partial, bound methods and ChinMap all do something complex with the underlying object. Dict-views are quite simple by comparison, is there an example where this would be helpful and better than just passing directly the mapping object?
msg370914 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2020-06-07 22:05
Do you see any easy way to have some_mapping_proxy.items().mapping return some_mapping_proxy rather than unmasking the proxied dict?
msg371070 - (view) Author: Dennis Sweeney (Dennis Sweeney) * (Python triager) Date: 2020-06-09 00:36
PR 20749 gives each dict view access to a mappingproxy for the original dict, although I don't know if that defeats the original purpose.

It might be hard to sensibly make MappingProxy(d).items() return something other than d.items(), since this is already the behavior for user-defined classes:

>>> class A:
    def __getitem__(self, key):
        return "value"
    def items(self):
        return 17

>>> from types import MappingProxyType
>>> MappingProxyType(A()).items()
17
msg371072 - (view) Author: Dennis Sweeney (Dennis Sweeney) * (Python triager) Date: 2020-06-09 02:01
Here's a workaround that's possible with PR 20749 applied:

>>> d = {"a":1, "b":2} # fill up the dict...
>>> DICT = object()
>>> d[DICT] = d
>>> items = d.items()
>>> del d
>>>
>>> d = items.mapping[DICT].pop(DICT)
>>> d
{'a': 1, 'b': 2}
msg371081 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2020-06-09 08:05
I meant that you can make dict_view.mapping always returning a MappingProxyType.
msg371121 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2020-06-09 16:20
> I meant that you can make dict_view.mapping always 
> returning a MappingProxyType.

That would be a reasonable constraint.
msg371155 - (view) Author: Inada Naoki (methane) * (Python committer) Date: 2020-06-10 02:46
+1.
msg371337 - (view) Author: Dennis Sweeney (Dennis Sweeney) * (Python triager) Date: 2020-06-12 08:05
Would it be better to have a dictview.mapping() method rather than an attribute, since it constructs a new object of a different type and since that's what keys(), values(), and items() are?
msg371398 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2020-06-12 17:19
New changeset 3ee0e48b0376a710c08eec6f30e4181563b192a2 by Dennis Sweeney in branch 'master':
bpo-40890: Add `mapping` property to dict views (GH-20749)
https://github.com/python/cpython/commit/3ee0e48b0376a710c08eec6f30e4181563b192a2
msg371506 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2020-06-14 15:29
There is a tiny portability issue.
msg371517 - (view) Author: Pablo Galindo Salgado (pablogsal) * (Python committer) Date: 2020-06-15 01:05
New changeset 10c3b2120afa01b2c310ac50e99d8b98c943b0a2 by Pablo Galindo in branch 'master':
bpo-40890: Fix compiler warning in dictobject.c (GH-20876)
https://github.com/python/cpython/commit/10c3b2120afa01b2c310ac50e99d8b98c943b0a2
History
Date User Action Args
2020-06-15 05:55:24serhiy.storchakasetstatus: open -> closed
stage: patch review -> resolved
2020-06-15 01:05:26pablogsalsetmessages: + msg371517
2020-06-15 00:41:24pablogsalsetnosy: + pablogsal
pull_requests: + pull_request20064
2020-06-14 20:54:40Dennis Sweeneysetstage: resolved -> patch review
pull_requests: + pull_request20061
2020-06-14 15:29:45serhiy.storchakasetstatus: closed -> open

messages: + msg371506
2020-06-12 17:20:04rhettingersetstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2020-06-12 17:19:29rhettingersetmessages: + msg371398
2020-06-12 08:05:32Dennis Sweeneysetmessages: + msg371337
2020-06-10 02:46:27methanesetmessages: + msg371155
2020-06-09 16:20:33rhettingersetmessages: + msg371121
2020-06-09 08:06:00serhiy.storchakasetmessages: + msg371081
2020-06-09 02:01:23Dennis Sweeneysetmessages: + msg371072
2020-06-09 00:36:03Dennis Sweeneysetmessages: + msg371070
2020-06-09 00:15:52Dennis Sweeneysetkeywords: + patch
pull_requests: + pull_request19954
2020-06-07 22:05:06rhettingersetkeywords: - patch, easy (C)

messages: + msg370914
2020-06-07 12:35:52remi.lapeyresetnosy: + remi.lapeyre
messages: + msg370893
2020-06-07 12:11:53methanesetnosy: + methane
messages: + msg370892
2020-06-07 10:57:10serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg370885
2020-06-07 10:37:41Dennis Sweeneysetmessages: + msg370884
2020-06-07 09:02:24Dennis Sweeneysetmessages: + msg370882
2020-06-07 08:56:02Dennis Sweeneysetkeywords: + patch
stage: patch review
pull_requests: + pull_request19906
2020-06-07 06:37:51Dennis Sweeneysetmessages: + msg370877
2020-06-07 06:28:49rhettingersetmessages: + msg370876
2020-06-07 06:20:51Dennis Sweeneysetnosy: + Dennis Sweeney
messages: + msg370875
2020-06-06 17:21:19rhettingercreate