Как обернуть каждый метод класса?
Я хотел бы обернуть каждый метод определенного класса в python, и я хотел бы сделать это, отредактировав код класса минимально. Как мне это сделать?
4 ответов
элегантный способ сделать это описан в Voidspace блоге Майкл Фурд в запись о том, что метаклассы являются и как их использовать в разделе Метод Декорирования Метакласса. Немного упростив его и применив к вашей ситуации, вы получили следующее:
from types import FunctionType
from functools import wraps
def wrapper(method):
@wraps(method)
def wrapped(*args, **kwrds):
# ... <do something to/with "method" or the result of calling it>
return wrapped
class MetaClass(type):
def __new__(meta, classname, bases, classDict):
newClassDict = {}
for attributeName, attribute in classDict.items():
if isinstance(attribute, FunctionType):
# replace it with a wrapped version
attribute = wrapper(attribute)
newClassDict[attributeName] = attribute
return type.__new__(meta, classname, bases, newClassDict)
class MyClass(object):
__metaclass__ = MetaClass # wrap all the methods
def method1(self, ...):
# ...etc ...
в Python декораторы функций/методов-это просто функциональные обертки плюс некоторый синтаксический сахар, чтобы сделать их использование легким (и более красивым).
Совместимость С Python 3 Update
предыдущий код использует Python 2.X metaclass синтаксис, который должен быть переведен для использования в Python 3.x, однако он больше не будет работать в предыдущей версии. Это означает, что ему нужно будет использовать:
class MyBase(metaclass=MetaClass)
...
вместо:
class MyBase(object):
__metaclass__ = MetaClass"
...
при желании можно написать код, совместимый как с Python 2, так и с Python 2.x и 3.x, но для этого требуется использовать несколько более сложную технику, которая динамически создает новый базовый класс, который наследует нужные метакласс, тем самым избегая ошибок из-за синтаксических различий между двумя версиями Python. Это в основном то, что Бенджамин Петерсон шесть модуль with_metaclass()
вы имеете в виду программно установить обертку на методы класса?? Ну, это, вероятно, действительно плохая практика, но вот как вы можете это сделать:
def wrap_methods( cls, wrapper ):
for key, value in cls.__dict__.items( ):
if hasattr( value, '__call__' ):
setattr( cls, key, wrapper( value ) )
если у вас есть класс, например
class Test( ):
def fire( self ):
return True
def fire2( self ):
return True
и оболочкой
def wrapper( fn ):
def result( *args, **kwargs ):
print 'TEST'
return fn( *args, **kwargs )
return result
затем вызов
wrap_methods( Test, wrapper )
применяется wrapper
до все методы, определенные в классе Test
. используйте с осторожностью! На самом деле, не используйте его вообще!
если требуется обширное изменение поведения класса по умолчанию, метаклассы-это путь. Вот альтернативный подход.
если ваш вариант использования ограничен только методами экземпляра обертывания класса, вы можете попробовать переопределить __getattribute__
магический метод.
from functools import wraps
def wrapper(func):
@wraps(func)
def wrapped(*args, **kwargs):
print "Inside Wrapper. calling method %s now..."%(func.__name__)
return func(*args, **kwargs)
return wrapped
обязательно используйте functools.wraps
при создании обертки, тем более, если обертка предназначена для отладки, поскольку она обеспечивает разумные трассировки.
import types
class MyClass(object): # works only for new-style classes
def method1(self):
return "Inside method1"
def __getattribute__(self, name):
attr = super(MyClass, self).__getattribute__(name)
if type(attr) == types.MethodType:
attr = wrapper(attr)
return attr
использование декораторов python-самый чистый способ сделать это, так как похоже, что вы хотите отладить или, по крайней мере, проследить код, который он появляется.