Restricting methods in derived classes

Mark McEahern marklists at mceahern.com
Fri Sep 13 22:45:14 EDT 2002


[Huaiyu Zhu]
> But seriously, your first solution actually contains all I needed.  Here's
> what I adopted in the end:

[snip]

> Maybe there is a simple way to wrap up restrict() in a metaclass, so that
> instead of restrict(foo) we can say class foo(dict, restricted): ...
> I'm still not so used to metaclasses to figure that out, however.

I like the solution you adopted and I used it to simplify (imo, anyway) my
metaclass approach (see below), which I still prefer, but I'm not quite sure
why.  One thing I don't quite understand is why you'd want to bother messing
with magic methods at all (which you're _common suggests you do).  That
seems like an invitation to disaster--but that's a statement that comes more
from ignorance than experience on my part.  In the code I attached, if you
wanted to suppress magic methods, you would simply remove the is_magic test.

Thanks for this opportunity to improve my understanding of metaclasses.

Cheers,

// mark

#!/usr/bin/env python

import inspect

def make_undefined(cls, attr):
    def undefined(self, *args, **kwargs):
        msg = "'%s' object has no attribute '%s'" % (cls.__name__, attr)
        raise AttributeError(msg)
    return undefined

def is_magic(attr):
    prefix = suffix = '__'
    return attr.startswith(prefix) and attr.endswith(suffix)

class Restricted(type):

    common = ['__class__', '__defined__', '__dict__', '__doc__',
              '__getattribute__', '__init__', '__new__']

    def __new__(cls, classname, bases, classdict):
        defined_key = 'defined'
        defined = classdict.get(defined_key, []) + Restricted.common

        # If a non-magic attribute from any base class is not in common or
        # defined, hide it with a method that raises a descriptive
        # AttributeError (mimicking Python).
        for base in bases:
            for k, v in inspect.getmembers(base):
                if not is_magic(k) and k not in defined:
                    classdict[k] = make_undefined(cls, k)
        return type.__new__(cls, classname, bases, classdict)

class RestrictedDict(dict):

    __metaclass__ = Restricted

    defined = ['put', 'get', 'keys', 'items', 'values']

f = RestrictedDict()
print f.keys
print f.keys()
f['a'] = 'b'
print f.keys()
try:
    f.clear()
except AttributeError:
    pass
print f.keys()

-





More information about the Python-list mailing list