3

So it's quite a simple question. how do I add __getitem__ to a Python module. I mostly just want it as an ease of use, however it's confusing why it wouldn't let me 'just set it'. Below is a simple example of __getitem__ semi-working, however I wish for the other['test'] to work.

Here's the full output:

hello
hello
Traceback (most recent call last):
  File "main.py", line 4, in <module>
    print other['test']
TypeError: 'module' object has no attribute '__getitem__'

main.py

import other
print other.get('test')
print other.__getitem__('test')
print other['test']

other.py

test = 'hello'

def __getitem__(name):
    return globals()[name]

get = __getitem__

I've tried to set __getitem__ using globals() aswell, globals()['__getitem__'] = __getitem__. It didn't work. And I tried to set it in main.py. So I'm confused as to why it's so adamant in not allowing me to use other['test'].

If it's impossible, then a short reason would be good.

Peilonrayz
  • 3,129
  • 1
  • 25
  • 37

2 Answers2

5

Special methods are looked up on the type, not on an instance. Python looks for type(other).__getitem__() and that isn't available. You'd have to add the __getitem__ method to the module type; you can't in Python.

You'd have to replace the whole module instance in sys.modules with an instance of your own class to achieve what you want:

class MyModule(object):
    def __init__(self, namespace):
        self.__dict__.update(namespace)
    def __getitem__(name):
        return self.__dict__[name]

import other
import sys
sys.modules[other.__name__] = MyModule(other.__dict__)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
4

This limitation doesn't just apply for modules, it applies for anything such that the type is not object or some subclass of object, or something with a metaclass that never bottoms out with object in the mro.

For example, you can also see this happening with type type:

In [32]: class Foo(type):
   ....:     pass
   ....: 

In [33]: type(Foo)
Out[33]: type

In [34]: Foo.__getitem__ = lambda x, y: x.__dict__.get(y)

In [35]: Foo.foo = "hello"

In [36]: Foo['foo']
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-38-e354ca231ddc> in <module>()
----> 1 Foo['foo']

TypeError: 'type' object has no attribute '__getitem__'

In [37]: Foo.__dict__.get('foo')
Out[37]: 'hello'

The reason is that at the C-API level, both module and type are particular instances of PyTypeObject which don't implement the required protocol for inducing the same search mechanism that the PyTypeObject implementation of object and friends does implement.

To change this aspect of the language itself, rather than hacking a replacement of sys.modules, you would need to change the C source definitions for PyModule_Type and PyType_Type such that there were C functions created for __getitem__ and added to the appropriate location in the C-API big PyTypeObject struct-o-magic-functions (a lot of which is expanded by the macro PyObject_HEAD) instead of 0 (which is the sentinel for does not exist), and recompile Python itself with these modified implementations of module and type.

ely
  • 74,674
  • 34
  • 147
  • 228