Python API - 创建可选括号的模式?

1 投票
2 回答
615 浏览
提问于 2025-04-16 20:53

我正在为一个Python库设计API,遇到了一些问题,感觉我的想法可能超出了Python的能力范围。

(如果有Python爱好者看到我设计中有点像Ruby的地方,我在这里先道个歉)

简单来说,我有一个类,里面有几个方法,每个方法都返回self,这样就可以把它们连在一起使用。所以:

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

这和下面的写法是一样的:

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

我想问的是,能不能让括号变成可选的。我了解了一些关于使用__getattr____call__来处理可调用对象的知识,但还没能完全实现。目前,我的类中的__getattr__会检查属性,然后判断name是方法还是属性(更准确地说,是可调用的还是不可调用的)。

我的问题是,如果它是一个可调用的对象,我需要让__getattr__做类似这样的事情:

>>> 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

这样它就返回了一个可调用的对象。不过,这样的话,我就无法让括号变成可选的,因为可调用对象会被返回,但永远不会被调用:

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

我想到的唯一方法是能够“向前看”代码,判断name后面是否跟着括号。如果name是可调用的但没有被调用,我就可以返回调用name时不带参数的结果,而不是返回一个可调用的对象。

我在想,这可能不太可行,但我还是想问问高手们的看法。

2 个回答

3

如果没有括号,你得到的是一个函数的引用;加上括号后,你得到的是函数执行后的结果。这是Python语法的一个基本部分(这种一致性是件好事!)。

你可以使用Python的property函数(或者相关的装饰器)来让一个方法在后台作为属性的别名,这样就能实现你想要的效果。不过,这样做可能意味着你写的代码会有很多副作用,这样是不太好的。

像这样链式调用在jQuery中很流行,因为jQuery的主要目的是在DOM中引发副作用。但在其他地方通常不太合适,因为这样会让你的应用变得很难调试和测试。

3

我觉得你想这样做有点疯狂。你有什么理由呢?那那些需要参数的方法怎么办?(见下面的例子。)

如果你用 __getattribute__ 而不是 __getattr__,对于没有参数的方法似乎是可以工作的。

这里有个可以运行的例子:

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

为了绕过参数的问题,你可以检查一下这个函数,只有在它不需要任何参数(除了self)时才调用它。可以参考这个链接:http://docs.python.org/library/inspect.html

为了你好,还是别继续走这条路了。

撰写回答