我目前正在使用EndpointsModel为AppEngine上的所有模型创建RESTful API。由于它是RESTful,这些api有很多重复代码,我想避免
例如:
class Reducer(EndpointsModel):
name = ndb.StringProperty(indexed=False)
@endpoints.api(
name="bigdata",
version="v1",
description="""The BigData API""",
allowed_client_ids=ALLOWED_CLIENT_IDS,
)
class BigDataApi(remote.Service):
@Reducer.method(
path="reducer",
http_method="POST",
name="reducer.insert",
user_required=True,
)
def ReducerInsert(self, obj):
pass
## and GET, POST, PUT, DELETE
## REPEATED for each model
我想让它们变得通用。所以我尝试动态添加方法到类。 到目前为止我尝试了什么:
from functools import partial, wraps
def GenericInsert(self, obj, cls):
obj.owner = endpoints.get_current_user()
obj.put()
return obj
# Ignore GenericDelete, GenericGet, GenericUpdate ...
import types
from functools import partial
def register_rest_api(api_server, endpoint_cls):
name = endpoint_cls.__name__
# create list method
query_method = types.MethodType(
endpoint_cls.query_method(
query_fields=('limit', 'pageToken'),
path="%ss" % name,
http_method="GET",
name="%s.list" % name,
user_required=True
)(partial(GenericList, cls=endpoint_cls)))
setattr(api_server, "%sList", query_method)
# create insert method
# ...
register_rest_api(BigDataApi, Reducer)
但我得到'functools.partial' object has no attribute '__module__' exception.
我认为这是因为endpoints.method
的装饰者和部分装扮者之间存在一些冲突。但不知道如何避免它。
Traceback (most recent call last):
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/runtime/wsgi.py", line 239, in Handle
handler = _config_handle.add_wsgi_middleware(self._LoadHandler())
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/runtime/wsgi.py", line 298, in _LoadHandler
handler, path, err = LoadObject(self._handler)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/runtime/wsgi.py", line 84, in LoadObject
obj = __import__(path[0])
File "/Users/Sylvia/gcdc2013/apis.py", line 795, in <module>
register_rest_api(BigDataApi, Reducer)
File "/Users/Sylvia/gcdc2013/apis.py", line 788, in register_rest_api
)(partial(GenericList, cls=endpoint_cls)))
File "/Users/Sylvia/gcdc2013/endpoints_proto_datastore/ndb/model.py", line 1544, in RequestToQueryDecorator
@functools.wraps(api_method)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/functools.py", line 33, in update_wrapper
setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'functools.partial' object has no attribute '__module__'
相关文章:
答案 0 :(得分:9)
我也偶然发现了这一点,我真的很惊讶,对我来说问题是部分对象缺少某些属性,特别是__module__
和__name__
默认使用wraps
使用functools.WRAPPER_ASSIGNMENTS
来更新属性,无论如何在python 2.7.6中默认为('__module__', '__name__', '__doc__')
,有几种方法可以解决这个问题... < / p>
更新仅显示属性...
import functools
import itertools
def wraps_safely(obj, attr_names=functools.WRAPPER_ASSIGNMENTS):
return wraps(obj, assigned=itertools.ifilter(functools.partial(hasattr, obj), attr_names))
>>> def foo():
... """ Ubiquitous foo function ...."""
...
>>> functools.wraps(partial(foo))(foo)()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/functools.py", line 33, in update_wrapper
setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'functools.partial' object has no attribute '__module__'
>>> wraps_safely(partial(foo))(foo)()
>>>
这里我们只是过滤掉所有那些不存在的属性。
另一种方法是严格仅处理部分对象,您可以使用wraps
折叠singledispatch
并创建包含的部分对象,其属性将从最深 func
属性。
有些事情:
import functools
def wraps_partial(wrapper, *args, **kwargs):
""" Creates a callable object whose attributes will be set from the partials nested func attribute ..."""
wrapper = wrapper.func
while isinstance(wrapper, functools.partial):
wrapper = wrapper.func
return functools.wraps(wrapper, *args, **kwargs)
def foo():
""" Foo function.
:return: None """
pass
>>> wraps_partial(partial(partial(foo)))(lambda : None).__doc__
' Foo Function, returns None '
>>> wraps_partial(partial(partial(foo)))(lambda : None).__name__
'foo'
>>> wraps_partial(partial(partial(foo)))(lambda : None)()
>>> pfoo = partial(partial(foo))
>>> @wraps_partial(pfoo)
... def not_foo():
... """ Not Foo function ... """
...
>>> not_foo.__doc__
' Foo Function, returns None '
>>> not_foo.__name__
'foo'
>>>
这稍微好一些,因为现在我们可以获得默认使用部分对象文档字符串之前的原始函数文档。
这可以修改为仅搜索当前部分对象是否还没有set属性,嵌套多个部分对象时应该稍快一些......
<强>更新强>
似乎python(CPython)3(至少3.4.3)没有这个问题,因为我不知道也不应该假设所有版本的python 3或其他实现如Jython也分享这个问题这是另一个未来准备方法
from functools import wraps, partial, WRAPPER_ASSIGNMENTS
try:
wraps(partial(wraps))(wraps)
except AttributeError:
@wraps(wraps)
def wraps(obj, attr_names=WRAPPER_ASSIGNMENTS, wraps=wraps):
return wraps(obj, assigned=(name for name in attr_names if hasattr(obj, name)))
有几点需要注意:
wraps
函数 只有当我们无法包装部分 时,以防python2或其他版本的未来版本修复此问题。< / LI>
wraps
复制文档和其他信息ifilter
,因为它已经在python3中删除了,我有和没有ifilter
的时间,但结果是不确定的,至少在python(CPython)2.7.6,无论哪种方式,差异都是微不足道的...... 答案 1 :(得分:2)
如果是由于&#34;包裹&#34;的问题引起的。在functools中,没有什么可以阻止你编写自己的部分而不会调用包装。根据python文档,这是partial的有效实现:
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*(args + fargs), **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
答案 2 :(得分:2)
在Python 3.5中,我发现在partial
中保留了对原始函数的引用。您可以.func
:
from functools import partial
def a(b):
print(b)
In[20]: c=partial(a,5)
In[21]: c.func.__module__
Out[21]: '__main__'
In[22]: c.func.__name__
Out[22]: 'a'
答案 3 :(得分:1)
我偶然发现了这一点,并且想到了我的解决方法。
正如@ samy-vilar python3正确提到的那样,没有这个问题。 我有一些使用functools.wrap的代码,需要在python2和python3上运行。
对于python2,我们使用functools32,这是python3的pyct2的functools的后端。这个包的wraps
实现完美无缺。另外它提供了lru_cache,它只在python3 functools中可用。
import sys
if sys.version[0] == '2':
from functools32 import wraps
else:
from functools import wraps
答案 4 :(得分:0)
在我们的案例中,我通过继承functools.partial来解决这个问题:
class WrappablePartial(functools.partial):
@property
def __module__(self):
return self.func.__module__
@property
def __name__(self):
return "functools.partial({}, *{}, **{})".format(
self.func.__name__,
self.args,
self.keywords
)
@property
def __doc__(self):
return self.func.__doc__
NB 你也可以使用__getattr__来重定向查询,但我认为这实际上不太可读(并且使用__name __插入任何有用的元数据变得更加困难)
答案 5 :(得分:0)
这里描述了一个非常方便的python 2.7解决方案:http://louistiao.me/posts/adding-name-and-doc-attributes-to-functoolspartial-objects/
即:
from functools import partial, update_wrapper
def wrapped_partial(func, *args, **kwargs):
partial_func = partial(func, *args, **kwargs)
update_wrapper(partial_func, func)
return partial_func
答案 6 :(得分:-1)
从Python 2.7.11开始修复此问题(不确定修复了哪个特定版本)。您可以在2.7.11中的functools.wraps
对象上执行functools.partial
。