17

Say I have a class with a bunch of methods:

class Human():

  def eat():
    print("eating")

  def sleep():
    print("sleeping")

  def throne():
    print("on the throne")

Then I run all the methods with

John=Human()
John.eat()
John.sleep()
John.throne()

I want to run print("I am") for each method being called. So I should get something like

I am:
eating
I am:
sleeping
I am:
on the throne

Is there a way to do this without having to reformat each method?

mmking
  • 1,564
  • 4
  • 26
  • 36
Pithikos
  • 18,827
  • 15
  • 113
  • 136
  • 8
    Use a decorator, applying it to all methods: [Attaching a decorator to all functions within a class](http://stackoverflow.com/q/3467526) and [Writing a class decorator that applies a decorator to all methods](http://stackoverflow.com/q/6695854) – Martijn Pieters Sep 25 '14 at 11:33
  • If you are trying to minimize the number of edits you need to make, the simplest thing to do is to just change the definition of each method. If you are looking for a way to isolate the change to a single place, to be applicable to any current or future method, decorators are the way to go. – chepner Sep 25 '14 at 12:09
  • Why not have a function `def do(self, action)`, then `eat` just becomes `self.do("eating")` and any further changes to the output happen in one place. – jonrsharpe Sep 25 '14 at 12:23
  • @jonrsharpe actually I did it that way. It seems the simplest way. I tried to solve it with decorators but it started looking like a monster so gave up. – Pithikos Sep 25 '14 at 12:30

3 Answers3

19

If you can't change how you call your methods you can use the __getattribute__ magic method (methods are attributes too remember!) you just have to be careful to check the type of attributes so you don't print "I am:" every time you want to access any string or int attributes you may have:

class Human(object):
    def __getattribute__(self, attr):
        method = object.__getattribute__(self, attr)
        if not method:
            raise Exception("Method %s not implemented" % attr)
        if callable(method):
             print "I am:"
        return method

    def eat(self):
        print "eating"

    def sleep(self):
       print "sleeping"

    def throne(self):
        print "on the throne"

John = Human()
John.eat()
John.sleep()
John.throne()

Outputs:

I am:
eating
I am:
sleeping
I am:
on the throne
nettux
  • 5,270
  • 2
  • 23
  • 33
  • 1
    The problem with this implementation occurs e.g. when an attribute is a function patched onto an instance, e.g. `my_instance.foo = lambda: 123`. I generally prefer checking using the `callable` built-in. – Berislav Lopac Apr 09 '19 at 10:21
  • @BerislavLopac good point! Updated the answer to use `callable` instead – nettux Apr 09 '19 at 11:11
3

You can do this if you don't mind adding an __init__ and __call__ method to your class and self to your method's arguments.

class Human():
    def __init__(self):
        return None
    def __call__(self, act):
        print "I am:"
        method = getattr(self, act)
        if not method:
            raise Exception("Method %s not implemented" % method_name)
        method()

    def eat(self):
        print "eating"

    def sleep(self):
        print "sleeping"

    def throne(self):
        print "on the throne"

John = Human()
John("eat")
John("sleep")
John("throne")

EDIT: see my other answer for a better solution

nettux
  • 5,270
  • 2
  • 23
  • 33
  • @Pithikos see my other answer for a solution that doesn't require you to change the way you call your methods – nettux Sep 26 '14 at 12:55
0

If you want to have arguments as well, you can try using metaprogramming to alter the class methods themselves to run a pre/post operation, like the answer to How to run a method before/after all class function calls with arguments passed?

Community
  • 1
  • 1
wonton
  • 7,568
  • 9
  • 56
  • 93