user1289853
user1289853

Reputation:

In Python, when should I use a meta class?

I have gone through this: What is a metaclass in Python?

But can any one explain more specifically when should I use the meta class concept and when it's very handy?

Suppose I have a class like below:

 class Book(object):
    CATEGORIES = ['programming','literature','physics']
    def _get_book_name(self,book):
        return book['title']
    def _get_category(self, book):
        for cat in self.CATEGORIES:
            if book['title'].find(cat) > -1:
                return cat  
        return "Other"


if __name__ == '__main__':
    b = Book()
    dummy_book = {'title':'Python Guide of Programming', 'status':'available'}
    print b._get_category(dummy_book)

For this class.

In which situation should I use a meta class and why is it useful?

Thanks in advance.

Upvotes: 7

Views: 1953

Answers (5)

Raymond Hettinger
Raymond Hettinger

Reputation: 226376

A metaclass is used whenever you need to override the default behavior for classes, including their creation.

A class gets created from the name, a tuple of bases, and a class dict. You can intercept the creation process to make changes to any of those inputs.

You can also override any of the services provided by classes:

  • __call__ which is used to create instances
  • __getattribute__ which is used to lookup attributes and methods on a class
  • __setattr__ which controls setting attributes
  • __repr__ which controls how the class is diplayed

In summary, metaclasses are used when you need to control how classes are created or when you need to alter any of the services provided by classes.

Upvotes: 4

mushfiq
mushfiq

Reputation: 1600

check the link Meta Class Made Easy to know how and when to use meta class.

Upvotes: -2

Ben
Ben

Reputation: 71495

Conceptually, a class exists to define what a set of objects (the instances of the class) have in common. That's all. It allows you to think about the instances of the class according to that shared pattern defined by the class. If every object was different, we wouldn't bother using classes, we'd just use dictionaries.

A metaclass is an ordinary class, and it exists for the same reason; to define what is common to its instances. The default metaclass type provides all the normal rules that make classes and instances work the way you're used to, such as:

  • Attribute lookup on an instance checks the instance followed by its class, followed by all superclasses in MRO order
  • Calling MyClass(*args, **kwargs) invokes i = MyClass.__new__(MyClass, *args, **kwargs) to get an instance, then invokes i.__init__(*args, **kwargs) to initialise it
  • A class is created from the definitions in a class block by making all the names bound in the class block into attributes of the class
  • Etc

If you want to have some classes that work differently to normal classes, you can define a metaclass and make your unusual classes instances of the metaclass rather than type. Your metaclass will almost certainly be a subclass of type, because you probably don't want to make your different kind of class completely different; just as you might want to have some sub-set of Books behave a bit differently (say, books that are compilations of other works) and use a subclass of Book rather than a completely different class.

If you're not trying to define a way of making some classes work differently to normal classes, then a metaclass is probably not the most appropriate solution. Note that the "classes define how their instances work" is already a very flexible and abstract paradigm; most of the time you do not need to change how classes work.

If you google around, you'll see a lot of examples of metaclasses that are really just being used to go do a bunch of stuff around class creation; often automatically processing the class attributes, or finding new ones automatically from somewhere. I wouldn't really call those great uses of metaclasses. They're not changing how classes work, they're just processing some classes. A factory function to create the classes, or a class method that you invoke immediately after class creation, or best of all a class decorator, would be a better way to implement this sort of thing, in my opinion.

But occasionally you find yourself writing complex code to get Python's default behaviour of classes to do something conceptually simple, and it actually helps to step "further out" and implement it at the metaclass level.

A fairly trivial example is the "singleton pattern", where you have a class of which there can only be one instance; calling the class will return an existing instance if one has already been created. Personally I am against singletons and would not advise their use (I think they're just global variables, cunningly disguised to look like newly created instances in order to be even more likely to cause subtle bugs). But people use them, and there are huge numbers of recipes for making singleton classes using __new__ and __init__. Doing it this way can be a little irritating, mainly because Python wants to call __new__ and then call __init__ on the result of that, so you have to find a way of not having your initialisation code re-run every time someone requests access to the singleton. But wouldn't be easier if we could just tell Python directly what we want to happen when we call the class, rather than trying to set up the things that Python wants to do so that they happen to do what we want in the end?

class Singleton(type):
    def __init__(self, *args, **kwargs):
        super(Singleton, self).__init__(*args, **kwargs)
        self.__instance = None

    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            self.__instance = super(Singleton, self).__call__(*args, **kwargs)
        return self.__instance

Under 10 lines, and it turns normal classes into singletons simply by adding __metaclass__ = Singleton, i.e. nothing more than a declaration that they are a singleton. It's just easier to implement this sort of thing at this level, than to hack something out at the class level directly.

But for your specific Book class, it doesn't look like you have any need to do anything that would be helped by a metaclass. You really don't need to reach for metaclasses unless you find the normal rules of how classes work are preventing you from doing something that should be simple in a simple way (which is different from "man, I wish I didn't have to type so much for all these classes, I wonder if I could auto-generate the common bits?"). In fact, I have never actually used a metaclass for something real, despite using Python every day at work; all my metaclasses have been toy examples like the above Singleton or else just silly exploration.

Upvotes: 6

Lauritz V. Thaulow
Lauritz V. Thaulow

Reputation: 50995

If you for whatever reason want to do stuff like Class[x], x in Class etc., you have to use metaclasses:

class Meta(type):
    def __getitem__(cls, x):
        return x ** 2

    def __contains__(cls, x):
        return int(x ** (0.5)) == x ** 0.5

# Python 2.x
class Class(object):
    __metaclass__ = Meta

# Python 3.x
class Class(metaclass=Meta):
    pass

print Class[2]
print 4 in Class

Upvotes: 1

thebjorn
thebjorn

Reputation: 27321

You use metaclasses when you want to mutate the class as it is being created. Metaclasses are hardly ever needed, they're hard to debug, and they're difficult to understand -- but occasionally they can make frameworks easier to use. In our 600Kloc code base we've used metaclasses 7 times: ABCMeta once, 4x models.SubfieldBase from Django, and twice a metaclass that makes classes usable as views in Django. As @Ignacio writes, if you don't know that you need a metaclass (and have considered all other options), you don't need a metaclass.

Upvotes: 9

Related Questions