[Tutor] Custom metaclass?

Andrei project5 at redrival.net
Fri Apr 20 08:41:22 CEST 2007


Michael Hannon <jmh <at> physics.ucdavis.edu> writes:
<snip>
> The approach, in essence, is a fairly standard one: redefine the 
> __setattr__ method to add some logic to enforce whatever restrictions 
> you choose, but the details are a bit convoluted.  Copyright law 
> probably prohibits me from posting the entire recipe, but here's the 
<snip>

Well, here's a dummy that simply ignores requests to add attributes, instead
printing a message about the attempt to the console, so we have a concrete
example to look at:

def no_new_attributes(*args, **kwargs):
    print 'called ', args, kwargs
    return no_new_attributes

class NoNewAttrs(object):
    __setattr__ = no_new_attributes(object.__setattr__)
    class __metaclass__(type):
        __setattr__ = no_new_attributes(type.__setattr__)
    
nna = NoNewAttrs()
#NoNewAttrs.__setattr__ = object.__setattr__
nna.x = 5
try:
    print 'object nna.x =' + str(nna.x)
except:
    print 'object nna.x doesn\'t exist'

NoNewAttrs.x = 5
try:
    print 'class nna.x =', nna.x
except:
    print 'class nna.x doesn\'t exist'

> Finally, my question: can somebody enlighten me as to how and why the 
> "custom metaclass",
> 
>      class __metaclass__(type):
> 
> does something useful?

A metaclass is to a class like a class is to an object. The way a class
customizes the behavior of an object, a metaclass customizes the behavior of a
class. It's all a bit mind-bending and I don't think I'm an expert on the
subject, but let's see how far we get.

Try running the example above with the  __metaclass__ code and without: you'll
see that if you leave it out, you can still add attributes to the class itself
(even though not to the object), so your console will say:

  object nna.x doesn't exist
  class nna.x = 5

If however you put the metaclass in, you'll get:

  object nna.x doesn't exist
  called  (<class '__main__.NoNewAttrs'>, 'x', 5) {}
  class nna.x = class nna.x doesn't exist

Without the metaclass code, it would be trivial to work around the __setattr__
limitation. Just try uncommenting the line "NoNewAttrs.__setattr__ =
object.__setattr__". If you disabled the metaclass, you'll see that nna.x will
be set. If the metaclass is enabled, it will not be set.

This being Python, you can still work around all this metaclass safety by adding
using this code:

NoNewAttrs.__metaclass__.__setattr__ = type.__setattr__
NoNewAttrs.__setattr__ = object.__setattr__
nna.x = 5
print 'After reset: nna.x =', nna.x

Run this and you'll see it will have restored the attribute-setting
functionality even if it's theoretically blocked. I'm not sure there is a way to
absolutely completely stop users from adding attributes.


Yours,

Andrei



More information about the Tutor mailing list