[Baypiggies] Trivial OOP pattern problem

David Ginsburg dav9dg at gmail.com
Fri Jun 15 09:30:24 CEST 2012


I agree, if you don't care about a given return value from some method
perhaps you'd want it to be "chainable", so I support your list of methods
idea.

>>> inspect.getmembers(cairo.Context, predicate=inspect.isbuiltin)
[('__new__', <built-in method __new__ of type object at 0x7f03a4d9f960>),
('__subclasshook__', <built-in method __subclasshook__ of type object at
0x7f03a4d9f960>)]
>>> inspect.getmembers(cairo.Context, predicate=inspect.isclass)
[('__class__', <type 'type'>)]

Only inspect.isbuiltin and inspect.isclass return non-empty lists, but if I
remove my startswith('__') check, __delattr__ gets through.
If I replace my startswith check with if isinstance(method,
(types.MethodType, types.FunctionType, types.BuiltinFunctionType,
types.BuiltinMethodType)): ...
the only methods that get through are:
__new__
__subclasshook__

I don't know enough about pycairo to know why this would be the case.

David

On Fri, Jun 15, 2012 at 12:14 AM, Brian Palmer <bpalmer at gmail.com> wrote:

> Well,  I think your approach would lead to better replability (e.g.,
> dir()), and be more efficient. I don't like the special-casing of None,
> though; it'd be better to have a list of methods not to override the return
> value of, probably, so that the default is to support chaining. The simple
> __getattr__ every time approach has the benefit of being simple; although
> perhaps it'd be worthwhile to also do   setattr(self, obj, myf) before
> returning myf.
>
> By the way, does inspect.isbuiltin  work for your decorator? I can't get
> pycairo to build, but it's possible you should be checking isinstance(foo,
> (types.MethodType, types.FunctionType, types.BuiltinFunctionType,
> types.BuiltinMethodType))
>
> (And of course that doesn't handle classes that are themselves callable).
>
>
>
> On Fri, Jun 15, 2012 at 12:04 AM, David Ginsburg <dav9dg at gmail.com> wrote:
>
>> Sorry for all the messages, but what I was doing was overkill.
>> After re-reading Brian's message I suppose you could just do this:
>>
>> def __getattr__(self, name):
>>     def myf(*args, **kwargs):
>>       to_return = getattr(self.ctx, name)(*args, **kwargs)
>>       if to_return is None:
>>         to_return = self
>>       return to_return
>>     return myf
>>
>> cheers,
>> David
>>
>>
>> On Fri, Jun 15, 2012 at 12:01 AM, David Ginsburg <dav9dg at gmail.com>wrote:
>>
>>> Hmm, so I just tried to actually run this and for some
>>> reason inspect.getmembers(cairo.Context, predicate=inspect.ismethod)
>>> returns an empty list.
>>>
>>> Here's my ugly workaround:
>>>
>>> def make_chainable_context(cls):
>>>
>>>     def return_self_decorator(func):
>>>
>>>         def wrapper(self, *args, **kwargs):
>>>             to_return = func(self, *args, **kwargs)
>>>             if to_return is None:
>>>                 to_return = self
>>>             return to_return
>>>         return wrapper
>>>
>>>     #for method_name, method in inspect.getmembers(cairo.Context,
>>> predicate=inspect.ismethod):
>>>     for method_name, method in inspect.getmembers(cairo.Context):
>>>         if not method_name.startswith('__'):
>>>             setattr(cls, method_name, return_self_decorator(method))
>>>      return cls
>>>
>>> Pretty sure this accomplishes what you wanted :).
>>>
>>> cheers,
>>> David
>>>
>>> On Thu, Jun 14, 2012 at 11:56 PM, David Ginsburg <dav9dg at gmail.com>wrote:
>>>
>>>> Hello,
>>>> The problem with this is if a method needs to return a value other than
>>>> self, you break it.
>>>> This is probably hacky, but you could accomplish what you want doing
>>>> the following:
>>>>
>>>> import inspect
>>>> import functools
>>>>
>>>> def make_chainable_context(cls):
>>>>
>>>>     def return_self_decorator(func):
>>>>
>>>>         def wrapper(self, *args, **kwargs):
>>>>             to_return = func(self, *args, **kwargs)
>>>>             if to_return is None:
>>>>                 to_return = self
>>>>             return to_return
>>>>         return wrapper
>>>>
>>>>     for method_name, method in inspect.getmembers(cairo.Context,
>>>> predicate=inspect.ismethod):
>>>>         setattr(cls, method_name, return_self_decorator(method))
>>>>     return cls
>>>>
>>>>
>>>> @make_chainable_context
>>>> class ChainableContext(cairo.Context):
>>>>     pass
>>>>
>>>> This way methods that return something other than None are not broken.
>>>>
>>>> cheers,
>>>> David
>>>>
>>>> On Thu, Jun 14, 2012 at 11:28 PM, Brian Palmer <bpalmer at gmail.com>wrote:
>>>>
>>>>> On Thu, Jun 14, 2012 at 10:52 PM, Ian Zimmerman <itz at buug.org> wrote:
>>>>>
>>>>>>
>>>>>> Isaac> Hi, Why do you need each of the methods to return the instance?
>>>>>> Isaac> Can you just wrap the method calls in a function and return the
>>>>>> Isaac> instance?
>>>>>>
>>>>>> I am not married to the idea of a wrapper class, but I don't
>>>>>> understand
>>>>>> what you propose instead.  What would the example code look like under
>>>>>> your scheme?
>>>>>>
>>>>>
>>>>> Your initial solution seems like it'd work:
>>>>> class Wrap(object):
>>>>>   def __init__(self, ctx):
>>>>>     self.ctx = ctx
>>>>>
>>>>>   def __getattr__(self, name):
>>>>>     def myf(*args, **kwargs):
>>>>>       getattr(self.ctx, name)(*args, **kwargs)
>>>>>       return self
>>>>>     return myf
>>>>>
>>>>> There are benefits to being less dynamic, but this might be good
>>>>> enough.
>>>>>
>>>>> _______________________________________________
>>>>> Baypiggies mailing list
>>>>> Baypiggies at python.org
>>>>> To change your subscription options or unsubscribe:
>>>>> http://mail.python.org/mailman/listinfo/baypiggies
>>>>>
>>>>
>>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/baypiggies/attachments/20120615/6e2be0e7/attachment.html>


More information about the Baypiggies mailing list