3

我正在编写一个 api,并且想知道执行以下操作的最 Pythonic 方式是什么。

我正在编写一堆方法来进行各种网络调用,参数主要转换为发布数据键和值。

到目前为止,我一直在写它的方式大多是这样的;

def doSomething(self,param1,param2,param3):
    payload={"param1":param1,
             "param2":param2,
             "param3":param3}
    return self.request("do/something",payload)

这已经具有必须重复可能更改的参数名称的缺点,但是这种模式还不错。

下面的案例让我想出更好的方法。在这种情况下,调用有 4 个可选参数

def doSomethingElse(self,param1,param2=None,param3=None,param4=None,param5=None):
    payload= {"param1":param1}
    if param2:
        payload["param2"]= param2
    if param3:
        payload["param3"]= param3
    # ... etc ...
    self.request("do/something/else",payload)

我的第一个想法是这样做:

def doSomethingElse(self,param1,**params):
    payload = {"param1":param1}
    payload.update(params)
    self.request("do/something/else",payload)

甚至:

def doSomethingElse(self,**payload):
    self.request("do/something/else",payload)

尽管第二个很好而且很简单,但是可以在没有非默认参数的情况下调用该方法。但是在这两种情况下,我在使用 api 时都会丢失方法签名,并且用户不会知道参数是什么(我知道我可以在文档字符串中编写预期的签名,但我宁愿防止发送拼写错误的关键字)。

我认为必须有一个很好的pythonic解决方案来做到这一点,有什么想法吗?

编辑

我认为我没有说清楚的一个关键点是参数是在调用中的 post 数据中发送的,我想确保只有这些键可以发送,就像在第一个示例中一样doSomethingElse,你可以' t 发送除这 5 个命名参数之外的任何内容。

4

4 回答 4

4

Pythonic 的方式是在调用函数时命名参数,而不是在函数签名中:

def doSomething(self, **kwargs):
    self.request("do/something/else", kwargs)

doSomething(param1=3, param2='one', param3=4)
于 2012-06-29T19:12:27.100 回答
1

Something like this, perhaps:

def doSomethingElse(self, param1, **params):
    payload = {"param1": param1}
    for name, value in params.items():
        if value is not None:
            payload[name] = value
    self.request("do/something/else", payload)
于 2012-06-29T19:01:55.423 回答
1

简单点怎么样

def get_payload(ldict):
    return {k:v for k,v in ldict.iteritems() if k != 'self' and v is not None}

class fred(object):
    some_class_var = 17
    def method(self, a, b=2):
        payload = get_payload(locals())
        print payload

这使

>>> f = fred()
>>> f.method()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method() takes at least 2 arguments (1 given)
>>> f.method(2)
{'a': 2, 'b': 2}
>>> f.method(2, b=3)
{'a': 2, 'b': 3}
>>> f.method(5, b=None)
{'a': 5}
>>> f.method(2, b=3, c=19)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method() got an unexpected keyword argument 'c'
>>> help(f.method)

Help on method method in module __main__:

method(self, a, b=2) method of __main__.fred instance

我认为符合您的标准。下一步将是使用装饰器(可能使用包装或装饰器模块来保留签名)以便计算有效负载然后传递,但我不知道是否@payload会比payload = get_payload(locals()). 请注意,使用locals()这种方式,需要在开始时完成。

不过,我认为这并不是防止不必要的核攻击的最佳方式。

于 2012-06-29T19:24:23.897 回答
0

如果您有几个这样的功能,您可以执行以下操作:

class Requester(object):
    def __init__(self, tobecalled, *allowed):
        self.tobecalled = tobecalled
        self.allowed = set(allowed)
    def __call__(self, otherobj, **k):
        for kw in k.iterkeys():
            if kw not in self.allowed:
                raise ValueError("unknown argument(s) given: %s" % kw)
        otherobj.request(self.tobecalled, **k)
    def __get__(self, outside, outsideclass):
        return lambda **k: self(outside, **k)

class Outside(object):
    def request(self, method, **k):
        print method, k
    do_one_thing = Requester("do/one/thing", 'param1', 'param2')
    do_nonsense = Requester("do/nonsense", 'param3')
    simple = Requester("simple")

o = Outside()
o.do_one_thing(param1=1, param2=2)
o.do_nonsense(param3=12)
o.simple()
try: o.do_one_thing(rparam1=1, param2=2)
except ValueError, e: print e
try: o.do_nonsense(gparam3=12)
except ValueError, e: print e
try: o.simple(whatever=12)
except ValueError, e: print e

这里会发生什么?我们创建了一个Requester扮演方法角色的对象:如果我们把它放在另一个类中(这里:)Outside,它可以被调用的方式,它也获得一个对象的引用,它被调用。我outside在这里所说的是“外部self”,正如我现在所说的那样。然后,它返回一个调用对象本身的 lambda,就像函数一样。在那里,检查参数的有效性,如果通过,我们就调用“ outside”的request()方法。

于 2012-06-29T21:52:51.067 回答