我有大约20种方法可以重定向到包含原始方法的包装器方法,以及其余的参数:
class my_socket(parent):
def _in(self, method, *args, **kwargs):
# do funky stuff
def recv(self, *args, **kwargs):
return self._in(super().recv, *args, **kwargs)
def recv_into(self, *args, **kwargs):
return self._in(super().recv_into, *args, **kwargs)
# and so on...
如何以编程方式添加更多这些方法?一切都开始出错之前,这就是我所能得到的:
for method in 'recv', 'recvfrom', 'recvfrom_into', 'recv_into', ...:
setattr(my_socket, method, ???)
我可以通过在类定义中分配,还是感觉更自然的其他东西来做到这一点?
class my_socket(parent):
def makes_recv_methods(name):
# wraps call to name
def recv_meh = makes_recv_methods('recv_meh')
我希望__get__
和朋友尽可能使用来自types
的魔术函数。
答案 0 :(得分:8)
我是通过在定义类之后运行一些代码从列表生成方法来实现的 - 你可以把它放到装饰器中。
import functools
def wrap_method(cls, name):
# This unbound method will be pulled from the superclass.
wrapped = getattr(cls, name)
@functools.wraps(wrapped)
def wrapper(self, *args, **kwargs):
return self._in(wrapped.__get__(self, cls), *args, **kwargs)
return wrapper
def wrap_methods(cls):
for name in cls.WRAP_ATTRS:
setattr(cls, name, wrap_method(cls, name))
return cls
@wrap_methods
class my_socket(parent_class):
WRAP_ATTRS = ['recv', 'recvfrom'] # ... + more method names
def _in(self, method, *args, **kwargs):
# do funky stuff
答案 1 :(得分:0)
wilberforce提案有效,但有一种更简单的方法只使用OOP:
def wrap_method(wrapped):
@functools.wraps(wrapped)
def wrapper(self, *args, **kwargs):
return self._in(wrapped.__get__(self, cls), *args, **kwargs)
return wrapper
class Parent:
def _in(self, method, *args, **kwargs):
return method(*args, **kwargs)
@wrap_method
def recv(self, *args, **kwargs):
return # whatever
@wrap_method
def recv_into(self, *args, **kwargs):
return # whatever
class MySocket(Parent):
def _in(self, method, *args, **kwargs):
# do funky stuff
答案 2 :(得分:0)
我想扩大已接受的答案。我希望可能有很长的装饰器方法列表应用于很长的方法列表。
import functools
def wrap_method(cls, name, wrapper_method_name):
# This unbound method will be pulled from the superclass.
wrapped = getattr(cls, name, wrapper_method_name)
@functools.wraps(wrapped)
def wrapper(self, *args, **kwargs):
wrapper_method = getattr(self, wrapper_method_name)
return wrapper_method(wrapped.__get__(self, cls), *args, **kwargs)
return wrapper
def wrap_methods(cls):
for wrapper_method_name in cls.WRAPPER_METHOD_NAMES:
for name in cls.WRAPPED_METHODS:
setattr(cls, name, wrap_method(cls, name, wrapper_method_name))
return cls
这是包装原始
的类@wrap_methods
class WrappedConnection(BaseConnection):
"""
This class adds some quality-of-life improvements to the BaseConnection class.
-WRAPPED_METHODS are wrapped by WRAPPER_METHOD_NAMES
-wrappers can be toggled on and off.
example:
connection = WrappedConnection(show_messages=True, log_errors=False, keep_authenticated=False)
default:
connection = WrappedConnection(show_messages=False, log_errors=True, keep_authenticated=True)
"""
WRAPPER_METHOD_NAMES = ['log_errors', 'keep_authenticated', 'show_messages']
WRAPPED_METHODS = ['a_method', 'b_method', 'c_method', 'd_method']
MESSAGE_OVERRIDE_MAP = {"a_method": "a_method_message_override_attribute",
"b_method": "b_method_message_override_attribute"}
def keep_authenticated(self, method, *args, **kwargs):
"""
If the session has expired, the session is re-authenticated. The incident is logged by the default logger.
This option can be turned off by setting keep_authenticated during initialization of a WrappedConnection object.
- connection = WrappedConnection(keep_authenticated=False) # why would you ever do this
:param method: (method) method to be wrapped
:param args: (args) passed args
:param kwargs: (kwargs) passed kwargs
:return: (method) method wrapped by @keep_authenticated
"""
response, expired_session = method(*args, **kwargs), None
if response["errors"] and self._keep_authenticated:
expired_session = list(filter(lambda x: 'expired session' in x, response["errors"]))
if expired_session:
self.__init__()
logging.info('Session has been re-authenticated.')
response = method(*args, **kwargs)
return response
def log_errors(self, method, *args, **kwargs):
"""
If there is an error the incident is logged. This option can be turned off by setting log_errors
during initialization of a WrappedConnection object.
- connection = WrappedConnection(log_errors=False)
:param method: (method) method to be wrapped
:param args: (args) passed args
:param kwargs: (kwargs) passed kwargs
:return: (method) method wrapped by @log_errors
"""
response = method(*args, **kwargs)
if response["errors"] and self._log_errors:
errors = response["errors"]
logging.error(errors)
return response
def show_messages(self, method, *args, **kwargs):
"""
Shows the xml that is sent during the request. This option can be turned on by setting show_messages during
initialization of a WrappedConnection object.
- connection = WrappedConnection(show_messages=True)
:param method: (method) method to be wrapped
:param args: (args) passed args
:param kwargs: (kwargs) passed kwargs
:return: (method) method wrapped by @show_messages
"""
response = method(*args, **kwargs)
if self._show_messages:
message_override_attr = WrappedConnection.MESSAGE_OVERRIDE_MAP.get(method.__name__)
if message_override_attr:
message_override = getattr(self, message_override_attr)
print(BeautifulSoup(message_override, "xml").prettify())
else:
self._show_message(method.__name__, *args, **kwargs)
return response
def __init__(self, *args, keep_authenticated=True, log_errors=True, show_messages=False, **kwargs):
super(WrappedConnection, self).__init__(*args, **kwargs)
self._keep_authenticated = keep_authenticated
self._log_errors = log_errors
self._show_messages = show_messages
答案 3 :(得分:-2)
您可以使用cog。
class MySocket(Parent):
"""[[[cog
import cog
l = ['in','out']
for item in l:
cog.outl("def _{0}(self, method, *args, **kwargs):".format(item))
]]]"""
#[[[end]]]
这有一个额外的好处,即易于更新,不会在最终评论之外触及您的代码,如果需要,您可以旋转生成的代码。
我已成功使用cog在另一个项目上生成样板文件,并与非生成代码混合使用。它开始将指令的输入文件读入字典。然后,对于样板的每个部分,它使用字典中的那一段来知道要写什么。
我在一个地方编辑指令文件,而不是样板文件中的二十个不同的地方。