开发者

Python API - pattern for creating optional parenthesis?

I am designing the API for a Python library, and have run into a situation where I think my imagination might have overtaken Python's considerable abilities.

(I want to apologize to any Pythonistas who may get offended by the Ruby-ness of my design)

Basically, there is a class with a few methods on it, each of which returns self, so that they can be chained together. So:

>>> a = MyKlass()
>>> b = a.method_one()
>>> c = b.method_two()

would be the same as

>>> a = MyKlass()
>>> c = a.method_one().method_two()

My question is whether it is possible to make the parenthesis optional. I have learned a bit about using __getattr__ and __call__ to manipulate callable objects, but haven't been able to fully implement this. Right now, I have the class's __getattr__ checking for the attribute, and then determining whether the name is a method or attribute (or more accurately, whether it is callable or not).

My problem is that if it is a callable, I need __getattr__ to do something like this:

>>> class MyKlass(object):
>>>     def __getattr__(self, name):
>>>         # pseudocode, you get the idea...
>>>         if name is callable:
>>>             def callme(*args, **kwds):
>>>                 # do stuff
>>>             return callme
>>>         else:
>>>             # just return the attribute

So it returns a callable object. This means, though, that I couldn't make the parenthesis optional, since the callable object would be returned, but never called:

>>> a = MyKlass()
>>> c = a.method_one.method_two    # AttributeError, since a bound function object does not have attribute "method_two"

The only way I thought of to do this would be to be able to "look ahead" in the code and determine whether the name is followed by parenthesis or not. Then, if name was callable but not called, I could return the results of calling name without arguments ins开发者_JAVA技巧tead of returning a callable object.

I am thinking that this is probably not doable, but I thought that I would ask the gurus anyway.


I think you're nuts for wanting to do this. What reason do you have? What about methods that take arguments? (See example below.)

It seems to work for methods without arguments if you use __getattribute__ instead of __getattr__.

Working example:

class Test(object):
    def __getattribute__(self, name):
        attr = super(Test, self).__getattribute__(name)
        if callable(attr):
            return attr()
        return attr
    def f(self):
        print 'f'
        return self
    def g(self):
        print 'g'
        return self
    def h(self, x):
        print x
        return self

t = Test()
t.f.g # Works fine
t.f().g # Fails - the parens aren't optional, they must be left off now
t.f.g.h(1) # Fails - unable to provide an argument

To get around the argument issue, you could inspect the function and only call it if it doesn't take any arguments (other than self). See: http://docs.python.org/library/inspect.html

Do yourself a favor and stop going down this path.


Without the parentheses, you have a reference to the function, with them you have the result of its invocation. This is a fundamental part of Python's syntax (this consistency is a good thing!).

You can always use Python's property function (or the associated decorator) to make a method work under the hood as an alias for a property which will achieve what you want. However, this suggests you are writing very side-effect-heavy code which is a Bad Idea.

Chaining like this is popular in jQuery because jQuery's sole purpose is to cause side effects in the DOM. Typically this isn't a sensible elsewhere because it makes your application very difficult to debug and test.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜