80

To implement a subscriptable object is easy, just implement __getitem__ in this object's class definition.
But now I want to implement a subscriptable class. For example, I want to implement this code:

class Fruit(object):
    Apple = 0
    Pear = 1
    Banana = 2
    #________________________________ 
    #/ Some other definitions,         \
    #\ make class 'Fruit' subscriptable. /
    # -------------------------------- 
    #        \   ^__^
    #         \  (oo)\_______
    #            (__)\       )\/\
    #                ||----w |
    #                ||     ||

print Fruit['Apple'], Fruit['Banana']
#Output: 0 2

I know getattr can do the same thing, but I feel subscript accessing is more elegant.

3
  • 4
    I have no answer for you, but I would like to know why? To me it looks like a regular Hash: >>> Fruit = {'Banana': 0, 'Apple': 1} >>> print Fruit['Banana'] 0 Commented Jul 13, 2012 at 10:57
  • @Pengman I also want to implement 'Fruit' as a enum type. if sth == Fruit.Apple: doSomething(). Commented Jul 13, 2012 at 11:13
  • 1
    Also, it seems to me that your real problem may require Python equivalent of "enums" and "constants" - tehre are several solutions for this in Python - check Pypi for "enum" - this one seems to be a favorite: pypi.python.org/pypi/flufl.enum/3.3.2 Commented Jul 13, 2012 at 12:09

5 Answers 5

63

Add something like this to your class:

class Fruit(object):
     def __init__(self):
         self.Fruits = {"Apple": 0, "Pear": 1, "Banana": 2}
     def __getitem__(self, item):
         return self.Fruits[item]
Sign up to request clarification or add additional context in comments.

Comments

45

Seems to work by changing the metaclass. For Python 2:

class GetAttr(type):
    def __getitem__(cls, x):
        return getattr(cls, x)

class Fruit(object):
    __metaclass__ = GetAttr

    Apple = 0
    Pear = 1
    Banana = 2

print Fruit['Apple'], Fruit['Banana']
# output: 0 2

On Python 3, you should use Enum directly:

import enum

class Fruit(enum.Enum):
    Apple = 0
    Pear = 1
    Banana = 2

print(Fruit['Apple'], Fruit['Banana'])
# Output: Fruit.Apple, Fruit.Banana
print(Fruit['Apple'].value, Fruit['Banana'].value)
# Output: 0 2

3 Comments

Thanks for your answer. Let me add some explanation: Class is also object in Python. The type of 'class object' is define by __metaclass__, default to type. So if you need a subscriptable class, just implement __getitem__ in its __metaclass__. For more information about __metaclass__, read this post.
@AbhiP Try the webarchive version, or this useful link which shows the same code for both python 2 and 3 metaclasses. Looks like Mike Watkins has temporarily taken down his article.
Some comprehensive explanation for Python 3: realpython.com/python-metaclasses/#custom-metaclasses
10

Expanding on @LuisKleinwort's answer, if you want to do this for all class attributes:

fruits_dict = {'apple':0, 'banana':1}

class Fruits(object):
    def __init__(self, args):
        for k in args:
            setattr(self, k, args[k])
            
    def __getitem__(self, item):
        return getattr(self, item)

fruits = Fruits(fruits_dict)
print(fruits.apple)
print(fruits['apple'])

Comments

10

I do think you were asking about subscripting to a class not an instance of a class.

Here is my answer to the question: "How to create a subscriptable class in Python?"

class Subscriptable:
    def __class_getitem__(cls, item):
        return cls._get_child_dict()[item]

    @classmethod
    def _get_child_dict(cls):
        return {k: v for k, v in cls.__dict__.items() if not k.startswith('_')}


class Fruits(Subscriptable):
    Apple = 0
    Pear = 1
    Banana = 2

>>> Fruits['Apple']
    0
>>> Fruits['Pear']
    1

Comments

1

Think of it this way: in your example, the definition of the class Fruit is what makes an object of the class Fruit subscriptable So to make the class on itself subscriptable you need to define it in the class definition that defines the class object, so in essence, the class of the class And a class of a class is a metaclass (which itself is just a class that inherits from type), giving you the final code like this:

class FruitsMeta(type):
    def __getitem__(self, item):
        return self.__dict__[item]

class Fruits(metaclass=FruitsMeta):
    Apple = 0
    Pear = 1
    Banana = 0

print(Fruits["Pear"]) # outputs 1

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.