Method behavior for user-created class instances

C

crazychimp132

Greetings.

I am looking for a way to achieve method behavior for a class I
created. That is, it has a __call__ method, so can be called like a
function. But I also want it to be treated as a method when it appears
in a class body.

Eg.

class foo:
def __call__(self, inst): pass

class bar:
meth = foo()

such that bar().meth() will not raise an exception for too few
arguments (because the inst argument in foo.__call__ is implicitly set
to the bar instance). I know this has to do with writing the __get__
method of foo, but I am wondering if there is perhaps some class I can
just inherit from to get the proper __get__, which behaves identically
to that of regular Python functions. The need for this arises out of
the implementation of a function decorator as a class.

Thanks.
 
C

crazychimp132

While it is not clear "why" you would want this, I believe this works.
If not, take a look at staticmethods or classmethods, they might work for you.

... def __call__(self, inst):
... print "foo.__call__", inst
...

... def __init__(self):
... self.foo = foo()
... self.meth = self.foo.__call__
...
foo.__call__ 1

-Larry

This doesn't work for me. I have a class which is used to decorate
functions, returning a callable object in the place of the original
function. So, instances of this class must be able to be used anywhere
a function would be used, and this means getting method behavior when
it is used in a class body.

A working implementation would be (in 3.0):

from functools import partial
from abc import ABCMeta, abstractmethod

class method_behavior(metaclass = ABCMeta):
def __get__(self, instance, owner):
if instance is None:
return self.__call__
return partial(self.__call__, instance)

@abstractmethod
def __call__(): pass

Then, any decorator class can inherit from it:

class foo(method_behavior):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwds):
print("calling decorated func")
return self.func(*args, **kwds)

Then, we can decorate a function with foo (via @foo) and it will work
either inside a class body or outside, it works everywhere an
undecorated function would work, eg.:

@foo
def bar():
print('bar')

class baz:
@foo
def bar(self):
print('bar')

What I am asking is whether there is a way to directly inherit method
behavior, instead of inexactly rewriting it as I did in
method_behavior.__get__().
 
B

bruno.desthuilliers

Greetings.

I am looking for a way to achieve method behavior for a class I
created. That is, it has a __call__ method, so can be called like a
function. But I also want it to be treated as a method when it appears
in a class body.

You need to implement the descriptor protocol the same way the
function type do.

import types

class Foo(object):
def __call__(self, instance):
print "%s - %s" % (self, instance)

def __get__(self, instance, cls):
return types.MethodType(self, instance, cls)

class Bar(object):
foo = Foo()

b = Bar()
b.foo()

I know this has to do with writing the __get__
method of foo, but I am wondering if there is perhaps some class I can
just inherit from to get the proper __get__, which behaves identically
to that of regular Python functions.

Extending types.FunctionType doesn't work OOTB (there's some
incompatibility wrt/ metaclasses)
 
C

crazychimp132

You need to implement the descriptor protocol the same way the
function type do.

import types

class Foo(object):
def __call__(self, instance):
print "%s - %s" % (self, instance)

def __get__(self, instance, cls):
return types.MethodType(self, instance, cls)

class Bar(object):
foo = Foo()

b = Bar()
b.foo()


Extending types.FunctionType doesn't work OOTB (there's some
incompatibility wrt/ metaclasses)

Thanks, this got me started in writing it for 3.0. There are no more
unbound methods in 3.0, so a check for whether instance is None is
necessary to give the right behavior. Here is the final implementation
I came up with:

from abc import ABCMeta, abstractmethod
from functools import update_wrapper
from types import MethodType

class decorator(metaclass = ABCMeta):
def __init__(self, function):
update_wrapper(self, function)
self.function = function

def __get__(self, instance, cls):
if instance is None:
return self
return MethodType(self, instance)

@abstractmethod
def __call__(): pass

To use it, write a class that inherits decorator and overrides
__call__, probably doing something with self.function.
 

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

No members online now.

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,810
Latest member
Kassie0918

Latest Threads

Top