creating classes with mix-ins
samwyse
samwyse at gmail.com
Tue May 12 07:45:41 EDT 2009
On May 11, 9:01 pm, Carl Banks <pavlovevide... at gmail.com> wrote:
> On May 11, 11:16 am, samwyse <samw... at gmail.com> wrote:
>
> > 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.
Unfortunately, 'boilerplate()' uses the handlers that I provide when
MetaBlog is instantiated. I tried the following, but it didn't work
(for reasons that were obvious in retrospect).
def instantiate_template(m_name, instance):
isAuthorised = instance.security.isAuthorised
method_to_wrap = getattr(instance.blog, m_name)
def template(self, which, username, password, *args):
if not isAuthorised(username, password, which, m_name):
raise Exception('Unauthorised access')
return method_to_wrap(which, *args)
template.__name__ = method_to_wrap.__name__
template.__doc__ = method_to_wrap.__doc__
return template
class MetaWeblog(object):
def __init__(self,
securityHandler=SimpleSecurityHandler,
blogHandler=SimpleBlogHandler):
self.security = securityHandler()
self.blog = blogHandler()
# from http://www.xmlrpc.com/metaWeblogApi
m_prefix = 'metaWeblog.'
m_list = ('newPost', 'editPost', 'getPost', 'newMediaObject',
'getCategories', 'getRecentPosts', )
# Here's where things fell apart
for m_name in m_list:
dotted_name = m_prefix + m_name
method = instantiate_template(m_name, self)
setattr(self, dotted_name, method)
So, I replaced that last for-loop with this:
# Since we're about to monkey-patch the class, we should
# make certain that all instances use the same handlers.
handlers = (self.security, self.blog)
try:
assert getattr(self.__class__, '_handlers') == handlers
except AttributeError:
for m_name in m_list:
dotted_name = m_prefix + m_name
method = instantiate_template(m_name, self)
setattr(self.__class__, dotted_name, method)
setattr(self.__class__, '_handlers', handlers)
This is good enough for now, since I can't conceive of a reason why
MetaBlog would be instantiated more than once. If it were, on the
other hand, it would probably be because you wanted to use different
handlers. In that case, I think I'd want to use a class factory,
something like this:
server.register_instance(
MetaWeblogFactory(securityHandler, blogHandler)()
)
Anyway, thanks for getting me over a conceptual hump.
More information about the Python-list
mailing list