我正在写一个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个命名参数之外的任何东西。
答案 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()
方法上进行调用。