如何为函数本身设置一个 repr?
How to set a repr for a function itself?
__repr__
用于return对象的字符串表示,但在Python中函数也是对象本身,而can have attributes.
如何设置函数的__repr__
?
我看到 here 可以在函数外部为函数设置属性,但通常在对象定义本身内设置 __repr__
,所以我想设置 repr在函数定义本身中。
我的用例是我正在使用 tenacity 重试具有指数退避的网络函数,我想记录我最后调用的函数的(信息性)名称。
retry_mysql_exception_types = (InterfaceError, OperationalError, TimeoutError, ConnectionResetError)
def return_last_retry_outcome(retry_state):
"""return the result of the last call attempt"""
return retry_state.outcome.result()
def my_before_sleep(retry_state):
print("Retrying {}: attempt {} ended with: {}\n".format(retry_state.fn, retry_state.attempt_number, retry_state.outcome))
@tenacity.retry(wait=tenacity.wait_random_exponential(multiplier=1, max=1200),
stop=tenacity.stop_after_attempt(30),
retry=tenacity.retry_if_exception_type(retry_mysql_exception_types),
retry_error_callback=return_last_retry_outcome,
before_sleep=my_before_sleep)
def connect_with_retries(my_database_config):
connection = mysql.connector.connect(**my_database_config)
return connection
目前 retry_state.fn
显示类似于 @chepner 所说的 <function <lambda> at 0x1100f6ee0>
,但我想向其中添加更多信息。
已经设置好了。
>>> repr(lambda x:x)
'<function <lambda> at 0x1100f6ee0>'
问题是 function
类型是不可变的,所以你不能只为 function.__repr__
分配一个新函数,你也不能创建 function
的子类型] 以覆盖 __repr__
。 (并不是说创建子类的实例很容易,即使可以定义它。)
您可以使用 returns 和 class 以及 __call__
和 __repr__
集的装饰器:
class CustomReprFunc:
def __init__(self, f, custom_repr):
self.f = f
self.custom_repr = custom_repr
def __call__(self, *args, **kwargs):
return self.f(*args, **kwargs)
def __repr__(self):
return self.custom_repr(self.f)
def set_repr(custom_repr):
def set_repr_decorator(f):
return CustomReprFunc(f, custom_repr)
return set_repr_decorator
@set_repr(lambda f: f.__name__)
def func(a):
return a
print(repr(func))
您不能对实际功能执行此操作; function
类型是不可变的,并且已经定义了一个 __repr__
,并且 __repr__
是在类型而不是实例上查找的,因此在给定函数上更改 __repr__
不会不要改变行为。
虽然在这种情况下可能没有用,但您可以创建自己的可调用对象class(类似于 C++ 仿函数),而那些 可以自己定义__repr__
。例如:
class myfunction:
@staticmethod # Avoids need to receive unused self
def __call__(your, args, here):
... do stuff and return as if it were a function ...
@classmethod # Know about class, but again, instance is useless
def __repr__(cls):
return f'{cls.__name__}(a, b, c)'
最后,您可以将其转换为 class 的单例实例(使其在使用方式上等同于普通函数):
myfunction = myfunction()
将 class 替换为 class 的单个实例。
注意:在实际代码中,我几乎可以肯定只是更改打印位置,以便在不修改函数的情况下以更有用的方式打印。与普通函数或包装的普通函数相比,这没有 太多 开销(因为我们将函数本身放在 __call__
中而不是包装,使其更快,但需要为每个“友好的 repr
函数”单独 class),但决定如何以 human-friendly 方式表示自己并不是函数的工作;那是你的工作,视情况而定。
您可以将 retry_state.fn
更改为 retry_state.__name__
。我使用了很多这样的装饰器。如果添加装饰器,每次调用感兴趣的函数时都会调用它。
def display_function(func):
""" This decorator prints before and after running """
@functools.wraps(func)
def function_wrapper(*args, **kwargs):
print(f'\nNow: Calling {func.__name__}.')
entity = func(*args, **kwargs)
print(f'Done: Calling {func.__name__}.\n')
return entity
return function_wrapper
此外,python 中的重试模块允许您执行一些默认情况下正在执行的操作。我经常使用装饰器:
import retrying
@retrying.retry(wait_exponential_multiplier=1000, wait_exponential_max=10000)
我认为自定义装饰器可以提供帮助:
import functools
class reprable:
"""Decorates a function with a repr method.
Example:
>>> @reprable
... def foo():
... '''Does something cool.'''
... return 4
...
>>> foo()
4
>>> foo.__name__
'foo'
>>> foo.__doc__
'Does something cool.'
>>> repr(foo)
'foo: Does something cool.'
>>> type(foo)
<class '__main__.reprable'>
"""
def __init__(self, wrapped):
self._wrapped = wrapped
functools.update_wrapper(self, wrapped)
def __call__(self, *args, **kwargs):
return self._wrapped(*args, **kwargs)
def __repr__(self):
return f'{self._wrapped.__name__}: {self._wrapped.__doc__}'
__repr__
用于return对象的字符串表示,但在Python中函数也是对象本身,而can have attributes.
如何设置函数的__repr__
?
我看到 here 可以在函数外部为函数设置属性,但通常在对象定义本身内设置 __repr__
,所以我想设置 repr在函数定义本身中。
我的用例是我正在使用 tenacity 重试具有指数退避的网络函数,我想记录我最后调用的函数的(信息性)名称。
retry_mysql_exception_types = (InterfaceError, OperationalError, TimeoutError, ConnectionResetError)
def return_last_retry_outcome(retry_state):
"""return the result of the last call attempt"""
return retry_state.outcome.result()
def my_before_sleep(retry_state):
print("Retrying {}: attempt {} ended with: {}\n".format(retry_state.fn, retry_state.attempt_number, retry_state.outcome))
@tenacity.retry(wait=tenacity.wait_random_exponential(multiplier=1, max=1200),
stop=tenacity.stop_after_attempt(30),
retry=tenacity.retry_if_exception_type(retry_mysql_exception_types),
retry_error_callback=return_last_retry_outcome,
before_sleep=my_before_sleep)
def connect_with_retries(my_database_config):
connection = mysql.connector.connect(**my_database_config)
return connection
目前 retry_state.fn
显示类似于 @chepner 所说的 <function <lambda> at 0x1100f6ee0>
,但我想向其中添加更多信息。
已经设置好了。
>>> repr(lambda x:x)
'<function <lambda> at 0x1100f6ee0>'
问题是 function
类型是不可变的,所以你不能只为 function.__repr__
分配一个新函数,你也不能创建 function
的子类型] 以覆盖 __repr__
。 (并不是说创建子类的实例很容易,即使可以定义它。)
您可以使用 returns 和 class 以及 __call__
和 __repr__
集的装饰器:
class CustomReprFunc:
def __init__(self, f, custom_repr):
self.f = f
self.custom_repr = custom_repr
def __call__(self, *args, **kwargs):
return self.f(*args, **kwargs)
def __repr__(self):
return self.custom_repr(self.f)
def set_repr(custom_repr):
def set_repr_decorator(f):
return CustomReprFunc(f, custom_repr)
return set_repr_decorator
@set_repr(lambda f: f.__name__)
def func(a):
return a
print(repr(func))
您不能对实际功能执行此操作; function
类型是不可变的,并且已经定义了一个 __repr__
,并且 __repr__
是在类型而不是实例上查找的,因此在给定函数上更改 __repr__
不会不要改变行为。
虽然在这种情况下可能没有用,但您可以创建自己的可调用对象class(类似于 C++ 仿函数),而那些 可以自己定义__repr__
。例如:
class myfunction:
@staticmethod # Avoids need to receive unused self
def __call__(your, args, here):
... do stuff and return as if it were a function ...
@classmethod # Know about class, but again, instance is useless
def __repr__(cls):
return f'{cls.__name__}(a, b, c)'
最后,您可以将其转换为 class 的单例实例(使其在使用方式上等同于普通函数):
myfunction = myfunction()
将 class 替换为 class 的单个实例。
注意:在实际代码中,我几乎可以肯定只是更改打印位置,以便在不修改函数的情况下以更有用的方式打印。与普通函数或包装的普通函数相比,这没有 太多 开销(因为我们将函数本身放在 __call__
中而不是包装,使其更快,但需要为每个“友好的 repr
函数”单独 class),但决定如何以 human-friendly 方式表示自己并不是函数的工作;那是你的工作,视情况而定。
您可以将 retry_state.fn
更改为 retry_state.__name__
。我使用了很多这样的装饰器。如果添加装饰器,每次调用感兴趣的函数时都会调用它。
def display_function(func):
""" This decorator prints before and after running """
@functools.wraps(func)
def function_wrapper(*args, **kwargs):
print(f'\nNow: Calling {func.__name__}.')
entity = func(*args, **kwargs)
print(f'Done: Calling {func.__name__}.\n')
return entity
return function_wrapper
此外,python 中的重试模块允许您执行一些默认情况下正在执行的操作。我经常使用装饰器:
import retrying
@retrying.retry(wait_exponential_multiplier=1000, wait_exponential_max=10000)
我认为自定义装饰器可以提供帮助:
import functools
class reprable:
"""Decorates a function with a repr method.
Example:
>>> @reprable
... def foo():
... '''Does something cool.'''
... return 4
...
>>> foo()
4
>>> foo.__name__
'foo'
>>> foo.__doc__
'Does something cool.'
>>> repr(foo)
'foo: Does something cool.'
>>> type(foo)
<class '__main__.reprable'>
"""
def __init__(self, wrapped):
self._wrapped = wrapped
functools.update_wrapper(self, wrapped)
def __call__(self, *args, **kwargs):
return self._wrapped(*args, **kwargs)
def __repr__(self):
return f'{self._wrapped.__name__}: {self._wrapped.__doc__}'