Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wrap every method of a class? [duplicate]

I'd like to wrap every method of a particular class in python, and I'd like to do so by editing the code of the class minimally. How should I go about this?

like image 531
rjkaplan Avatar asked Jul 05 '12 17:07

rjkaplan


People also ask

How do you decorate a class method?

To decorate a method in a class, first use the '@' symbol followed by the name of the decorator function. A decorator is simply a function that takes a function as an argument and returns yet another function. Here, when we decorate, multiply_together with integer_check, the integer function gets called.

How do you create a metaclass in Python?

To create your own metaclass in Python you really just want to subclass type . A metaclass is most commonly used as a class-factory. When you create an object by calling the class, Python creates a new class (when it executes the 'class' statement) by calling the metaclass.

What is a wrapper class python?

In the decorator body, wrapper class modifies the class C maintaining the originality or without changing C. cls(x) return an object of class C (with its name attribute initialized with the value of x). The method get_name return the name attribute for the wrap object.

What are class wrap up activities and why are they important?

Class wrap up activities can be used to encourage students to reflect on the material they have learned during the class period. These activities are also useful for the instructor; even if they aren’t graded, they allow the instructor to check for understanding.

What are the values of a wrapper in Python?

Values of Wrapper objects (printing as objects) Byte object g1: 12 Integer object m1: 58 Float object f1: 9.6 Double object r1: 60.5 Unwrapped values (printing as data types) byte value, bv: 12 int value, iv: 58 float value, fv: 9.6 double value, dv: 60.5 Press any key to continue . . . 140. Wrapper class Wrapping and Unwrapping example 2 141.

What to do in the final minutes of class?

The final minutes of class are the perfect time to ask students to draw connections between what they’ve learned and how it can be applied in other settings. Rather than rushing to pack up their belongings, students can do activities such as creating a list of ways that the day’s material applies outside of class.


1 Answers

An elegant way to do it is described in Michael Foord's Voidspace blog in an entry about what metaclasses are and how to use them in the section titled A Method Decorating Metaclass. Simplifying it slightly and applying it to your situation resulted in this:

from functools import wraps from types import FunctionType  def wrapper(method):     @wraps(method)     def wrapped(*args, **kwargs):     #   ... <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 ... 

In Python, function/method decorators are just function wrappers plus some syntactic sugar to make using them easy (and prettier).

Python 3 Compatibility Update

The previous code uses Python 2.x metaclass syntax which would need to be translated in order to be used in Python 3.x, however it would then no longer work in the previous version. This means it would need to use:

class MyClass(metaclass=MetaClass)  # apply method-wrapping metaclass     ... 

instead of:

class MyClass(object):     __metaclass__ = MetaClass  # wrap all the methods     ... 

If desired, it's possible to write code which is compatible with both Python 2.x and 3.x, but doing so requires using a slightly more complicated technique which dynamically creates a new base class that inherits the desired metaclass, thereby avoiding errors due to the syntax differences between the two versions of Python. This is basically what Benjamin Peterson's six module's with_metaclass() function does.

from types import FunctionType from functools import wraps  def wrapper(method):     @wraps(method)     def wrapped(*args, **kwargs):         print('{!r} executing'.format(method.__name__))         return method(*args, **kwargs)     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)   def with_metaclass(meta):     """ Create an empty class with the supplied bases and metaclass. """     return type.__new__(meta, "TempBaseClass", (object,), {})   if __name__ == '__main__':      # Inherit metaclass from a dynamically-created base class.     class MyClass(with_metaclass(MetaClass)):         @staticmethod         def a_static_method():             pass          @classmethod         def a_class_method(cls):             pass          def a_method(self):             pass      instance = MyClass()     instance.a_static_method()  # Not decorated.     instance.a_class_method()   # Not decorated.     instance.a_method()         # -> 'a_method' executing 
like image 76
martineau Avatar answered Sep 18 '22 14:09

martineau