将方法参数输入dict的Pythonic方法

时间:2012-06-29 18:42:44

标签: python

我正在写一个api,并且想知道做以下事情的最佳方式是什么。

我正在编写一系列方法来进行各种Web调用,这些参数主要转化为后期数据键和值。

到目前为止我写作的方式大多是这样的;

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解决方案来做这个,任何想法?

修改

我认为我没有说清楚的一个关键点是,参数是在调用中的后期数据中发送的,我想确保只发送这些密钥,就像在{的第一个示例中一样{1}},你不能发送除这5个命名参数之外的任何东西。

4 个答案:

答案 0 :(得分:4)

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

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

doSomething(param1=3, param2='one', param3=4)

答案 1 :(得分:1)

这样的事情,也许是:

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)

答案 2 :(得分: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(),需要在开始时完成。

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

答案 3 :(得分: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()方法上进行调用。