Python Runtime Method Call Binding

K

k3xji

Hi,

Is there a way to hook a function call in python? I know __getattr__
is doing for variables, it is giving us a chance before a field is
initialized. Do we have same functionality for methods?

Example:

class Foo(object):
def __call_method__(self, ...) # just pseudo
print 'A method is called in object...'

f = Foo()
f.test_method()

I think you understand my point.

Thanks,
 
B

Bruno Desthuilliers

k3xji a écrit :
Hi,

Is there a way to hook a function call in python? I know __getattr__
is doing for variables, it is giving us a chance before a field is
initialized.

Note that since the introduction of the "new-style" object model - that
is, in Python 2.2 -, computed attributes are better handled with the
descriptor protocol, and specially the general purpose 'property' class.
The __getattr__ hook should only be used when you want to handle read
access to an attribute that doesn't exist at all (ie : automatic
delegation etc).

Also note that a method is mostly a computed attribute (another
application of the descriptor protocol FWIW)...
Do we have same functionality for methods?

Which "functionality" ? What do you want to do ? automatically delegate
method calls, or "wrap" method calls so you can ie log them or attach
more behaviour ?
Example:

class Foo(object):
def __call_method__(self, ...) # just pseudo
print 'A method is called in object...'

f = Foo()
f.test_method()

Ok, I guess this is the second case. The answer is "decorator".

def log(func):
def _logged(*args, **kw):
print "func", func.__name__, " called with ", args, kw
return func(*args, **kw)
_logged.__name__ = "logged_%s" % func.__name__
_logged.__doc__ = func.__doc__
return _logged

class Foo(object):
@log
def method(self, yadda=None):
print "in Foo.method, yadda = ", yadda
return yadda

f = Foo()
f.method()
f.method(42)


HTH
 
D

Diez B. Roggisch

k3xji said:
Hi,

Is there a way to hook a function call in python? I know __getattr__
is doing for variables, it is giving us a chance before a field is
initialized. Do we have same functionality for methods?

Example:

class Foo(object):
def __call_method__(self, ...) # just pseudo
print 'A method is called in object...'

f = Foo()
f.test_method()

I think you understand my point.

The special method __call__ is your friend.


class SomethingCallable(object):

def __call__(self):
print "I'm called, and you not!"

SomethingCallable()()

Diez
 
S

Steven D'Aprano

Hi,

Is there a way to hook a function call in python? I know __getattr__ is
doing for variables,

What do you mean "variables"? Do you mean attributes?

it is giving us a chance before a field is
initialized.

What field? Is a field the same as a variable, or something else?

Do we have same functionality for methods?

Yes, methods are attributes too, they are reached by the exact same
mechanism as any other attribute.

Any object with a __call__ method can be called. Normally you create
objects with a __call__ method by using def or lambda.

Example:

class Foo(object):
def __call_method__(self, ...) # just pseudo
print 'A method is called in object...'

f = Foo()
f.test_method()

I think you understand my point.

I wish I did. It might have helped if you actually showed the expected
output of your "test_method", instead of expecting us to *guess* what you
expect to happen.

I'm going to guess what you mean, and show you one possible solution.
Read it carefully: it shows three examples of __getattr__, starting from
the simplest to the relatively complicated.


class Parrot(object):
def __getattr__(self, name):
if name == "colour":
return "red"
elif name == "talk":
return lambda x: "Hi, I'm %s the talking parrot" % x
elif name == "speak":
class Birdy(object):
def __init__(self, name):
self.name = name
def __call__(self):
return "I'm the talking Parrot %s" % self.name
return Birdy('Fred')
else:
raise AttributeError


And in use:
"I'm the talking Parrot Fred"
 
K

k3xji

What do you mean "variables"? Do you mean attributes?


What field? Is a field the same as a variable, or something else?


Yes, methods are attributes too, they are reached by the exact same
mechanism as any other attribute.

Any object with a __call__ method can be called. Normally you create
objects with a __call__ method by using def or lambda.





I wish I did. It might have helped if you actually showed the expected
output of your "test_method", instead of expecting us to *guess* what you
expect to happen.

I'm going to guess what you mean, and show you one possible solution.
Read it carefully: it shows three examples of __getattr__, starting from
the simplest to the relatively complicated.

class Parrot(object):
    def __getattr__(self, name):
        if name == "colour":
            return "red"
        elif name == "talk":
            return lambda x: "Hi, I'm %s the talking parrot" % x
        elif name == "speak":
            class Birdy(object):
                def __init__(self, name):
                    self.name = name
                def __call__(self):
                    return "I'm the talking Parrot %s" % self.name
            return Birdy('Fred')
        else:
            raise AttributeError

And in use:


"Hi, I'm Fred the talking parrot"


<__main__.Birdy object at 0x820076c>>>> p.speak()

"I'm the talking Parrot Fred"

Thank you for all the answers. I have given a bad example but got
great answers. So let me clarify my problem again.

I think "decorator of Bruno" and the "third example of Steven" is what
I am looking for.

Steven you embed a callable object in __getattr__ to accomplish what I
want. But my problem:
My problem is that I have a very-multithreaded application that I want
to profile dynamically. And I want to see how many functions are
called/how much time spent on them.(I tried Profilers but lack of
multithreading support I cannot do what I want to do.)

So, from your answers above, if I use a decorator I will not be able
to understand that the function has finished processing. And if I use
the "callable object" Steven's third solution, I will need to convert
every function I want to profile to a callable object.

I think problem is clarified now. Maybe you have other suggestions?

Thanks,
 
B

Bruno Desthuilliers

k3xji a écrit :
(snip)
My problem is that I have a very-multithreaded application that I want
to profile dynamically. And I want to see how many functions are
called/how much time spent on them.(I tried Profilers but lack of
multithreading support I cannot do what I want to do.)

???

I didn't found any mention of such a limitation on the profile /
cProfile doc:
http://www.python.org/doc/2.6/library/profile.html#module-cProfile
So, from your answers above, if I use a decorator I will not be able
to understand that the function has finished processing.

Yes you will:

def log(func):
def _logged(*args, **kw):
print "func", func.__name__, " called with ", args, kw
result = func(*args, **kw)
print "func", func.__name__, " returned ", result
return result

_logged.__name__ = "logged_%s" % func.__name__
_logged.__doc__ = func.__doc__
return _logged
> I think problem is clarified now. Maybe you have other suggestions?


Yes.

1/ the

@decorator
def func():
pass

syntax is really syntactic sugar for

def func():
pass

func = decorator(func)


2/ nothing prevents you from dynamically rebinding methods at runtime:

def log(func):
# cf below
if getattr(func, "_decorated", False):
# already decorated, just return the func or method as is
return func

def _logged(*args, **kw):
print "func", func.__name__, " called with ", args, kw
result = func(*args, **kw)
print "func", func.__name__, " returned ", result
return result

_logged.__name__ = "logged_%s" % func.__name__
_logged.__doc__ = func.__doc__

# IMPORTANT : mark the function / method as already decorated
_logged._decorated = True

return _logged


class Foo(object):
def bar(self):
pass

Foo.bar = log(Foo.bar)

3/ Now fact is that even all this won't probably be enough to implement
a robust generic profiler - something which obviously requires a deep
understanding of the language *and* it's implementation.
 
K

k3xji

Thanks for the tips.
3/ Now fact is that even all this won't probably be enough to implement
a robust generic profiler - something which obviously requires a deep
understanding of the language *and* it's implementation.

Indeed. Maybe not generic,but with these information at least I can
write my specific multi-threaded profiler. All I need to is to rebind
the methods/functions in the given modules and guard the callCount and
timeElapsed values via locks. And the difficult point here is the
timeElapsed information which should also take into account the thread
it is running on, I can specifically differentiate the threads by
looking at the args[0] parameter which is holding the self instance.
Of course for this to work, I need to limit which objects to
rebind..etc.etc.

Briefly, I think I got what I asked for:)

Thanks,
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,433
Messages
2,571,683
Members
48,796
Latest member
Greg L.

Latest Threads

Top