23

Suppose following class:

class Class(object):
    @classmethod
    def getitem(*args):
        print 'getitem %s' % (args,)
    @classmethod
    def __getitem__(*args):
        print '__getitem__ %s' % (args,)

The getitem method behaves as expected: it receives Class as first arg, but __getitem__ receives type as first arg:

calling Class.getitem(test)
getitem (<class '__main__.Class'>, 'test')

calling obj.getitem(test)
getitem (<class '__main__.Class'>, 'test')

calling Class[test]
'type' object has no attribute '__getitem__'

calling obj[test]
__getitem__ (<class '__main__.Class'>, 'test')

What is the magic there behind __getitem__ ?

1
  • 1
    Note: Python 3.7 introduced __class_getitem__ to make this easily possible. Commented Oct 3, 2024 at 16:10

2 Answers 2

29

Special methods are looked up on the class, and not on the instance - unlike regular methods that are looked up on the instance first. See Special method lookup in the Python data model docs.

Thinking about Class as an instance of type, this means when you do

Class.getitem(test)

It looks first for exactly what you tell it: a method in Class's own attributes called getitem. But, when you use

Class[test]

it skips this, and goes straight to type (being the class of Class, or its metaclass), and so calls type.__getitem__(Class, test). So, what's happening isn't that __getitem__ gets type as its first argument (it would still get Class, as it does if you explicitly Class.__getitem__(test)), its that the __getitem__ that Python looks for in this case doesn't exist. To make it exist, you need to define your own metaclass for Class that defines it as an instance method, rather than defining it on Class as a classmethod.

Sign up to request clarification or add additional context in comments.

1 Comment

Thinking about Class as an instance of type. Thank you. Thank you. Thank you.
20

When you call x[test], the interpreter inspects type(x) for the __getitem__ attribute. In case of Class[test] it's the Class's metaclass, i.e. type. If you want to have a class-wide __getitem__, define it inside a new metaclass. (Needless to say, that's a sort of magic, as anything you do with metaclasses)

class Meta(type):
    def __getitem__(self, arg):
        print "__getitem__:", arg


class X(object):
    __metaclass__ = Meta

X['hello'] # output: __getitem__ hello

1 Comment

Thats's the solution for the question stated, but it's too much coding for such syntactic sugar. And in actual code i'm trying to getitem on metaclass itself :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.