Сделайте что-то в начале и конце методов
Есть ли простой способ сделать что-то в начале и конце каждой функции в классе? Я просмотрел __getattribute__
, но не думаю, что могу использовать его в этой ситуации?
Вот упрощенная версия того, что я пытаюсь сделать:
class Thing():
def __init__(self):
self.busy = False
def func_1(self):
if self.busy:
return None
self.busy = True
...
self.busy = False
def func_2(self):
if self.busy:
return None
self.busy = True
...
self.busy = False
...
Ответы
Ответ 1
Вы можете использовать декораторы (если вы не знаете их, вы можете обратиться к PEP-318):
def decorator(method):
def decorated_method(self, *args, **kwargs):
# before the method call
if self.busy:
return None
self.busy = True
# the actual method call
result = method(self, *args, **kwargs)
# after the method call
self.busy = False
return result
return decorated_method
class Thing():
def __init__(self):
self.busy = False
@decorator
def func_1(self):
...
@decorator
def func_2(self):
...
Возможно, вы захотите использовать functools.wraps
, если хотите, чтобы декорированный метод выглядел как оригинальный метод. @decorator
- это просто синтаксический сахар, вы также можете применить декоратор явно:
class Thing():
def __init__(self):
self.busy = False
def func_1(self):
...
func_1 = decorator(func_1) # replace "func_1" with the decorated "func_1"
Если вы действительно хотите применить его ко всем методам, вы можете дополнительно использовать декоратор класса:
def decorate_all_methods(cls):
for name, method in cls.__dict__.items():
if name.startswith('_'): # don't decorate private functions
continue
setattr(cls, name, decorator(method))
return cls
@decorate_all_methods
class Thing():
def __init__(self):
self.busy = False
def func_1(self):
...
def func_2(self):
...
Ответ 2
В качестве альтернативы принятому ответу, если вы хотите, чтобы это украшение применимо только для методов экземпляра, вы можете использовать __getattribute__
.
class Thing(object):
def __init__(self):
self.busy = False
def __getattribute__(self, name):
attr = object.__getattribute__(self, name)
if callable(attr) and not name.startswith('_') and attr.__self__ == self:
attr = decorator(attr)
return attr
def func_1(self):
# instance method will be wrapped by `decorator`
...
@classmethod
def class_func(cls):
# class method will not be wrapped by `decorator`
# when called using `self.`, `cls.` or `Thing.`.
...
@staticmethod
def static_func():
# static method will not be wrapped by `decorator`
# when called using `Thing.`.
...
- Для этого требуется
object
и не будет работать для классов старого стиля в Python 2.
-
callable
был удален в Python 3.0, но вернулся в 3.2. В качестве альтернативы можно использовать isinstance(obj, collections.Callable)
.
Если вы хотите по-разному обернуть методы класса и статические методы, вы можете наследовать от пользовательского type
metaclass
:
class Meta(type):
def __getattribute__(*args):
print("staticmethod or classmethod invoked")
return type.__getattribute__(*args)
class Thing(object, metaclass=Meta):
...
def __getattribute__(self, name):
attr = object.__getattribute__(self, name)
if callable(attr) and not name.startswith('_'):
if attr.__self__ == self:
attr = decorator(attr)
else:
attr = Meta.__getattribute__(Thing, name)
return attr
Вышеупомянутый metaclass=Meta
является синтаксисом Python 3. В Python 2 он должен быть определен как:
class Thing(object):
__metaclass__ = Meta