我为SOAP请求创建了一个类。我当时看到我做同样的事情,所以我用不同的逻辑和值编写相同的方法。类似的例子如下:
class Client(object):
def get_identity(self, identity):
context = {'identity': identity}
return self.post_request('get_identity', context)
def get_degree(self, degree, date):
context = {
'degree': degree,
'date': date
}
return self.post_request('get_degree', context)
def send_response(self, status, degree):
context = {
'degree': degree,
'status': status,
'reason': 'Lorem ipsum dolor sit amet.'
}
return self.post_request('send_response', context)
def post_request(self, method, args):
headers = "Creating headers"
data = "Mapping hear with method and args arguments"
response = requests.post(self.wsdl, data=data.encode('UTF-8'), headers=headers)
root = objectify.fromstring(response.content)
objectify.deannotate(root, cleanup_namespaces=True)
return root
然后我调用一个方法:
client = Client()
self.client.get_identity(identity)
当我想要实现新的SOAP方法时,我将写下这个:
def get_status(self, id):
context = {'id': id}
return self.post_request('get_status', context)
正如我上面提到的,我的目标是防止这种重复,我不知道该怎么做。
答案 0 :(得分:2)
您可以使用装饰器来实现此目的。这样,您可以以与您一直非常相似的方式指定方法,但是在保留文档字符串和参数规范的同时消除冗余。
要构建您始终创建的context
字典,使用等于参数名称的键,我们会dict(zip(argspec, args))
。
我们使用一个包装器来装饰SOAP方法,该包装器在post_request
上调用self
,方法名称为第一个参数,context
为第二个参数。
因为您还需要始终添加的静态参数,我们会处理装饰器的关键字参数,如this blog post by Carson Myers中所述。
from decorator import decorator
from inspect import getargspec
def soap_method(f = None, **options):
if f:
@decorator
def wrapper(method, client, *args):
argspec = getargspec(method).args[1:]
context = dict(zip(argspec, args))
context.update(options)
return client.post_request(method.__name__, context)
return wrapper(f)
else:
return lambda f: soap_method(f, **options)
class Client:
@soap_method
def get_identity(self, identity):
"""Documentation works."""
pass
@soap_method
def get_degree(self, degree, date):
pass
@soap_method(reason='Lorem ipsum dolor sit amet.')
def send_response(self, status, degree):
pass
def post_request(self, method, context):
print method, context
AFAIK,您无法使用docstrings和kindall的解决方案,而在此处:
>>> help(Client().get_identity)
Help on method get_identity in module foo:
get_identity(self, identity) method of foo.Client instance
Documentation works.
使用这种方法的另一个好处是,如果某些方法需要动态修改上下文,可以将这两行添加到wrapper
:
dynamic = method(client, *args)
if dynamic: context.update(dynamic)
您可以定义根据参数计算事物的方法,而不是返回静态值:
@soap_method
def get_identity(self, identity):
return {'hash': hash(identity)}
最后,如果你没有decorator
模块,那么你可以改用它:
from functools import wraps
from inspect import getargspec
def soap_method(f = None, **options):
if f:
@wraps(f)
def wrapper(client, *args):
argspec = getargspec(f).args[1:]
context = dict(zip(argspec, args))
context.update(options)
return client.post_request(f.__name__, context)
return wrapper
else:
return lambda f: soap_method(f, **options)
这仍然会保留文档字符串,但不会像decorator
那样保留argspec。
答案 1 :(得分:1)
编写一个为您编写函数的函数。
def req(methname, *names, **extra):
def do(self, *values):
context = dict(zip(names, values))
context.update(extra)
return self.post_request(methname, context)
# create wrapper with proper signature
f = eval("lambda self, %(p)s: do(self, %(p)s)" % {"p": ",".join(names)}, {"do": do})
f.__name__ = methname
return f
class Soapy(object):
def post_request(self, method, args):
headers = "Creating headers"
data = "Mapping hear with method and args arguments"
response = requests.post(self.wsdl, data=data.encode('UTF-8'), headers=headers)
root = objectify.fromstring(response.content)
objectify.deannotate(root, cleanup_namespaces=True)
return root
class Client(Soapy):
get_identity = req("get_identity", "identity")
get_degree = req("get_degree", "degree", "date")
send_response = req("send_response", "status", "degree", reason="Lorem ipsum")
这是唯一一个非常棘手的部分,如果你对它们help()
进行签名是很有用的,我通过评估包含包装函数的字符串来做到这一点。在Python 3.3+中,您可以构建一个签名对象,而不使用包装器。
答案 2 :(得分:1)
此答案使用partial
method of functools
并实施__getattr__
。当您说client.get_identity
之类的内容时,会返回一个函数,然后使用任何其他参数对其进行评估,例如(status, degree)
。
from functools import partial
class Client(object):
def __getattr__(self, attr):
# This is called after normal attribute lookup fails
# For this example, assume that means 'attr' should be a SOAP request
return partial(self.post_request, attr)
def post_request(self, method, **context):
# Fill in with actual values
headers = "Method %s"%method
data = "arguments",context
print "Header: %s\nData: %s"%(headers, data)
response = requests.post(self.wsdl, data=data.encode('UTF-8'), headers=headers)
root = objectify.fromstring(response.content)
objectify.deannotate(root, cleanup_namespaces=True)
return root
这可以像:
一样使用client = Client()
client.get_identity(id="id", status="status")
输出:
Header: Method get_identity
Data: ('arguments', {'status': 'status', 'id': 'id'})