MetaClasses

you probably shouldn't use them

2017-09

by

Kurt Neufeld

MetaClasses

  • what are they
  • why do you need them
  • why you almost never need them

But First!


      x = 1
      
      x = int(1)
      
      type(x) # <type 'int'>
      
      type(int) # ?
      

More But First!


      class MyClass(object): # always use object in 2.7
          pass
      
      c = MyClass()
      
      type(c) # <class '__main__.MyClass'>
      
      
      type(MyClass) # <type 'type'>
      

what happened to object?

A Bit More First!


      type(int) # <type 'type'>

      type(object) # <type 'type'>

brain starts exploding here


      type(type) # <type 'type'>

okay wtf?

wut?

just as c is an instance of MyClass

MyClass is an instance of type

It's turtles all the way down, until you get to type

type is an instance of type

Lets make a class!


      name = 'MyClass'
      bases = (object,)
      _dict = {'x':1}

      MyClass = type(name, bases, _dict)
      

      type(MyClass) # <type 'type'>

      MyClass.x       # 1
      c = MyClass()
      c.x             # 1
      c.x = 2
      MyClass.x       # 1
      c.x             # 2
      

We're already metaprogramming

So we used code to make a class.

That's cool

Why would you want to do this?

Who here has used Django? SQLAlchemy?

The only reason I've come across to use metaclasses is model definition when you wan to talk to a database

There's probably several others

I bet Dave knows a few

Why would you want to do this?

When you want a new class with lots of functionality that almost identical to another class but it has nothing in common with it

same same but different

General Pattern


      class MetaClass(type):

        # this called once per derived class _definition_
        def __new__(meta_class, name, bases, attrs):
          new_class = type(name, bases, {})
          self.do_things_to_class(new_class, attrs)
          return new_class

        # creates a new instance of derived class
        def __call__(cls, *args, **kw):
            obj = cls.__new__(cls, *args, **kw)
            self.do_things_to_instance(obj)
            obj.__init__(*args, **kw)
            return obj
      

      class MyClass(object):
          __metaclass__ = MetaClass
      

That doesn't look too bad...

Let's go spelunking!

Good resources

  • https://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/
  • https://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example
  • https://jakevdp.github.io/blog/2012/12/01/a-primer-on-python-metaclasses/

The End

questions?