{"action":"created","issue":{"url":"https://api.github.com/repos/mne-tools/mne-python/issues/2765","repository_url":"https://api.github.com/repos/mne-tools/mne-python","labels_url":"https://api.github.com/repos/mne-tools/mne-python/issues/2765/labels{/name}","comments_url":"https://api.github.com/repos/mne-tools/mne-python/issues/2765/comments","events_url":"https://api.github.com/repos/mne-tools/mne-python/issues/2765/events","html_url":"https://github.com/mne-tools/mne-python/pull/2765","id":124775610,"number":2765,"title":"[MRG] Automatic info['nchan'] and info['ch_names']","user":{"login":"wmvanvliet","id":428273,"avatar_url":"https://avatars.githubusercontent.com/u/428273?v=3","gravatar_id":"","url":"https://api.github.com/users/wmvanvliet","html_url":"https://github.com/wmvanvliet","followers_url":"https://api.github.com/users/wmvanvliet/followers","following_url":"https://api.github.com/users/wmvanvliet/following{/other_user}","gists_url":"https://api.github.com/users/wmvanvliet/gists{/gist_id}","starred_url":"https://api.github.com/users/wmvanvliet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wmvanvliet/subscriptions","organizations_url":"https://api.github.com/users/wmvanvliet/orgs","repos_url":"https://api.github.com/users/wmvanvliet/repos","events_url":"https://api.github.com/users/wmvanvliet/events{/privacy}","received_events_url":"https://api.github.com/users/wmvanvliet/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/mne-tools/mne-python/milestones/8","html_url":"https://github.com/mne-tools/mne-python/milestones/0.12","labels_url":"https://api.github.com/repos/mne-tools/mne-python/milestones/8/labels","id":1429321,"number":8,"title":"0.12","description":"","creator":{"login":"Eric89GXL","id":2365790,"avatar_url":"https://avatars.githubusercontent.com/u/2365790?v=3","gravatar_id":"","url":"https://api.github.com/users/Eric89GXL","html_url":"https://github.com/Eric89GXL","followers_url":"https://api.github.com/users/Eric89GXL/followers","following_url":"https://api.github.com/users/Eric89GXL/following{/other_user}","gists_url":"https://api.github.com/users/Eric89GXL/gists{/gist_id}","starred_url":"https://api.github.com/users/Eric89GXL/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Eric89GXL/subscriptions","organizations_url":"https://api.github.com/users/Eric89GXL/orgs","repos_url":"https://api.github.com/users/Eric89GXL/repos","events_url":"https://api.github.com/users/Eric89GXL/events{/privacy}","received_events_url":"https://api.github.com/users/Eric89GXL/received_events","type":"User","site_admin":false},"open_issues":32,"closed_issues":35,"state":"open","created_at":"2015-11-25T13:36:18Z","updated_at":"2016-02-05T17:05:07Z","due_on":"2016-04-29T04:00:00Z","closed_at":null},"comments":65,"created_at":"2016-01-04T15:10:00Z","updated_at":"2016-02-08T10:58:19Z","closed_at":"2016-02-03T14:02:07Z","pull_request":{"url":"https://api.github.com/repos/mne-tools/mne-python/pulls/2765","html_url":"https://github.com/mne-tools/mne-python/pull/2765","diff_url":"https://github.com/mne-tools/mne-python/pull/2765.diff","patch_url":"https://github.com/mne-tools/mne-python/pull/2765.patch"},"body":"The info object has two redundant fields: `nchan` and `ch_names`. They are there as convenience fields. However, whenever the `chs` list is updated, these fields need to be manually updated as well.\r\n\r\nThis PR makes these fields behave more like properties.\r\n\r\nIt does so by making `Info` a subclass of `collections.MutableMapping`, which allows it to redefine `__setitem__` and `__getitem__` while retaining full compatibility with the default Python dict.\r\n\r\nThe `nchan` field just maps to `len(info['chs'])`.\r\n\r\nThe `ch_names` field is a bit more tricky. From the outside, it behaves as a mapping to `[ch['ch_name'] for ch in info['chs']]`. However, in order not to generate a new list every time the field is accessed, the field is an instance of `_ChannelNameList`.\r\n\r\nThe `_ChannelNameList` class is a subclass of `collections.Sequence`, thus implementing a list that is read-only, but otherwise fully compatible with a normal Python list. It overwrites the `__getitem(self, index)__` method to map to `info['chs'][index]['ch_name']` on the fly. It also defines a neat `make_index_map()` function that generates a dictionary that maps channel names to integer indices, which can be used to speed things up when many lookups are needed.\r\n\r\nThe rest of the code is updated to no longer set the `nchan` and `ch_names` fields of Info objects.\r\n\r\nCloses #2300 "},"comment":{"url":"https://api.github.com/repos/mne-tools/mne-python/issues/comments/181307098","html_url":"https://github.com/mne-tools/mne-python/pull/2765#issuecomment-181307098","issue_url":"https://api.github.com/repos/mne-tools/mne-python/issues/2765","id":181307098,"user":{"login":"dengemann","id":1908618,"avatar_url":"https://avatars.githubusercontent.com/u/1908618?v=3","gravatar_id":"","url":"https://api.github.com/users/dengemann","html_url":"https://github.com/dengemann","followers_url":"https://api.github.com/users/dengemann/followers","following_url":"https://api.github.com/users/dengemann/following{/other_user}","gists_url":"https://api.github.com/users/dengemann/gists{/gist_id}","starred_url":"https://api.github.com/users/dengemann/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dengemann/subscriptions","organizations_url":"https://api.github.com/users/dengemann/orgs","repos_url":"https://api.github.com/users/dengemann/repos","events_url":"https://api.github.com/users/dengemann/events{/privacy}","received_events_url":"https://api.github.com/users/dengemann/received_events","type":"User","site_admin":false},"created_at":"2016-02-08T10:58:19Z","updated_at":"2016-02-08T10:58:19Z","body":"Yes, you could say it like that. I think we should just be really careful\nwhen touching public APIs as the Info. If you're using MNE in many programs\nyou're happy over anything that does *not* change. MNE is not so young\nanymore, we have many applications and users and it would be a good idea to\nassume that whatever is public is in our responsibility and should be\nmaintained accordingly. People --including myself-- will use it, use cases\nemerge from public APIs, whether primarily intended or not. If you then say\nit's worth a deprecation cycle or not for whatever reasons, you will have\nto assume that somewhere out there other people will be crying and / or\ncursing for dealing with unexpected crashes and whatever results from\nglobal public API changes instead of moving on with what they actually\nwanted to spend their time on. I cannot really remember any issues caused\nby the old API btw. But maybe I'm selectively oblivious.\n\nThe other thing is that we should really generally try to avoid complexity\nin objects. We already ramped up a good amount of it in the past. The\nreason why I'm ranting here is that I hope I can make you think about this\na bit. I'm afraid that if we have PRs like this, we generate precedents and\nsoon we will start implementing extra API rules and conditional behaviors\nwherever a potential bug might be lurking ... to protect our \"user\" who\nvery often will be a good Python programmer anyways and has already written\nlots of code using the existing API.\nOk, in this case you put forward good reasons, and maybe there is a\n\"generation problem\" to it. Indeed as Marijn suggested, I never looked up\n'nchan' and instead would always look up the len of the attribute. 'nchan'\nto me was a historical relict that we need to write fiff files. Yes this is\nhow it used to be. I appreciated attempts to simplify things and it is a\ngood argument that info should be consistent and used effectively. Maybe\nanother less complex solution might have been to consider a FrozenDict. It\nis a standard lib object and it would have been less complex to\ncharacterise (\"no field can be changed\"). Of course in that case we would\nneed an explicit update mechanism, maybe via subclassing or simply as a\nfunction that re-constructs the info explicitly by constructing it new and\ncopying old values. Keep in mind that any object class you introduce\n(because it does not exist in the Python world already) is yet another\nthing to learn.\n\nI would be personally very happy if we could agree on seeing this one\nrather as a rare event and if we could join our main efforts on\ndeconvoluting things where possible and focusing on API stability,\ndocumented functionality and performance.\n\n\nOn Mon, Feb 8, 2016 at 11:19 AM, jona-sassenhagen <notifications@github.com>\nwrote:\n\n> I think Denis is in fact more concerned with the process than the outcome\n> right?\n>\n> —\n> Reply to this email directly or view it on GitHub\n> <https://github.com/mne-tools/mne-python/pull/2765#issuecomment-181293759>\n> .\n>\n"}}