Re: creating classes with mix-ins



On May 11, 11:16 am, samwyse <samw...@xxxxxxxxx> wrote:
I'm writing a class that derives it's functionality from mix-ins.
Here's the code:

    def boilerplate(what):   # This used to be a decorator, but all of
the
        ##what = f.__name__  # function bodies turned out to be
'pass'.
        'Validate the user, then call the appropriate plug-in.'
        def template(self, which, username, password, *args):
            if not self.security.isAuthorised(username, password,
which, what):
                raise Exception('Unauthorised access')
            return getattr(self.blog, what)(which, *args)
        template.__name__ = what
        template.__doc__ = getattr(self.blog, what).__doc__
        return template

    class MetaWeblog(object):
        def __init__(self,
                     securityHandler=SimpleSecurityHandler,
                     blogHandler=SimpleBlogHandler):
            self.security = securityHandler()
            self.blog = blogHandler()
        newPost = boilerplate('newPost')
        editPost = boilerplate('editPost')
        getPost = boilerplate('getPost')
        # etc, etc, etc

I'd like to replace the method definitions with a loop:
        for what in attr_list:
            setattr(klass, what, boilerplate(what))

That begs the question of where I define 'klass' and 'attr_list'.
Should I use a class decorator, or a metaclass?

Here's the thing: unless you have advance knowledge of the methods
defined by self.blog, you can't get the attr_list at class definition
time, which means neither the metaclass nor the decorator would be a
good approach. If that's the case, you should define newPost,
editPost, and whatever other methods of self.blog as ordinary
attributes of self, within the init function. boilerplate would be
the same except you would pass self to it and allow template to use it
from its nested scope (it would no longer be an instance method since
it's an ordinary attribute).

If you do know what the methods of self.blog will be, then that's
where you get attr_list from. So, for instance, if blogHandler always
returns an object of type Blog, then you could inspect Blog's type
dict to see what methods are defined in it; in fact you probably want
to check the whole MRO for Blog, like this (untested):

attr_list = []
for cls in Blog.__mro__:
for value in cls.__dict__:
if is_wrapped_method(value):
attr_list.append(value)


A metaclass is probably overkill to assign the wrapped blog methods.
I probably wouldn't even bother with the decorator, and just write the
loop after the class definition. Then you can use MetaBlog directly
for klass.

class MetaBlog(object):
...

for what in attr_list:
setattr(MetaBlog, what, boilerplate(what))


If it were the kind of thing I found myself doing often I'd refactor
into a decorator.


Carl Banks
.



Relevant Pages

  • Re: Mangle function name with decorator?
    ... possibly by using a decorator to mark them. ...   def foo(... ... dictionary that the metaclass gets only has one one entry, ... def get_only: ...
    (comp.lang.python)
  • Re: decorator to fetch arguments from global objects
    ... We already set that db in the request object to make it easy to pass it ... if both don't exist raise an exception ...   if db is None: ... Yes that's exactly why I want a decorator, ...
    (comp.lang.python)
  • Re: doctests and decorators
    ... recommended fix is to update the order of checks in the _from_module ... method of DocTestFinder in the doctest module. ... module of the function object and not of the decorator. ...     return new_function ...
    (comp.lang.python)
  • Re: Removing inheritance (decorator pattern ?)
    ... Naturally, the decorator pattern ... could end up with as many Strategy classes as subclasses, ...
    (comp.lang.python)
  • Re: Profiling weirdness: Timer.timeit(), fibonacci and memoization
    ... takes fiband fibfrom memory and adds them together. ... The problem is that the un-decorated, loop only version takes ... the decorator it takes less that a second. ... the function (fib) only gets called once. ...
    (comp.lang.python)