Overriding methods per-object

P

Pavel Panchekha

I've got an object which has a method, __nonzero__
The problem is, that method is attached to that object not that class
a = GeneralTypeOfObject()
a.__nonzero__ = lambda: False
a.__nonzero__() False

But:

bool(a)
True

What to do?
 
C

Chris Rebert

I've got an object which has a method, __nonzero__
The problem is, that method is attached to that object not that class

True

This is as documented. See
http://docs.python.org/3.0/reference/datamodel.html#special-lookup
What to do?

Either wrap the object in another object of a class that does define
__bool__, or change GeneralTypeOfObject so it defines __bool__(),
possibly having its __bool__ delegate to another method, which you
could then override on a per-object basis similar to your current
code.

Cheers,
Chris
 
A

Aaron Brady

I've got an object which has a method, __nonzero__
The problem is, that method is attached to that object not that class


True

What to do?

The docs don't say you can do that:

Special method names
A class can implement certain operations that are invoked by special
syntax (such as arithmetic operations or subscripting and slicing) by
defining methods with special names.

Should you be able to?
 
P

Pavel Panchekha

The docs don't say you can do that:

Thanks, hadn't noticed that.
Should you be able to?

I'd say so. In my case, I need a class that can encapsulate any
object, add a few methods to it, and spit something back that works
just like the object, but also has those extra methods. I can't just
add the methods, because it has to work on e.g. lists. So I'll have to
end up defining all the possible methods on that class (and that's
still not best because I can't use hasattr to test if, for example,
addition is allowed on that object).

On the other hand, I see how this severely restricts the possibly
optimizations that can be made in the interpreter.
 
A

Aaron Brady

Thanks, hadn't noticed that.


I'd say so. In my case, I need a class that can encapsulate any
object, add a few methods to it, and spit something back that works
just like the object, but also has those extra methods. I can't just
add the methods, because it has to work on e.g. lists. So I'll have to
end up defining all the possible methods on that class (and that's
still not best because I can't use hasattr to test if, for example,
addition is allowed on that object).

On the other hand, I see how this severely restricts the possibly
optimizations that can be made in the interpreter.

Can you dynamically subclass it:

def subclasser( obj ):
class newclass( obj.__class__ ):
def __nonzero__.
obj.__class__= newclass

FYI, the __class__ member is assignable, though I've personally never
done it in practice.
 
S

Steven D'Aprano

I've got an object which has a method, __nonzero__ The problem is, that
method is attached to that object not that class

True

What to do?

(1) Don't do that.

(2) If you *really* have to do that, you can tell the class to look at
the instance:

class GeneralTypeOfObject(object):
def __nonzero__(self):
try:
return self.__dict__['__nonzero__']
except KeyError:
return something

(3) But a better solution might be to subclass the class and use an
instance of that instead. You can even change the class on the fly.

a.__class__ = SubclassGeneralTypeOfObject # note the lack of brackets
 
G

George Sakkis

I've got an object which has a method, __nonzero__
The problem is, that method is attached to that object not that class


True

What to do?


FYI this works as you expect if GeneralTypeOfObject is an old-style
class (i.e. does not inherit from object). If this feature is so more
important than all those that come with new-style classes, you have
control over the involved classes and don't care about Python 3.x
(where old-style classes are gone), you may choose to downgrade
GeneralTypeOfObject to old-style.

George
 
P

Piet van Oostrum

Pavel Panchekha said:
PP> Thanks, hadn't noticed that.
PP> I'd say so. In my case, I need a class that can encapsulate any
PP> object, add a few methods to it, and spit something back that works
PP> just like the object, but also has those extra methods. I can't just
PP> add the methods, because it has to work on e.g. lists. So I'll have to
PP> end up defining all the possible methods on that class (and that's
PP> still not best because I can't use hasattr to test if, for example,
PP> addition is allowed on that object).
PP> On the other hand, I see how this severely restricts the possibly
PP> optimizations that can be made in the interpreter.

But you can give each object its own class and then put the special
methods in that class:
.... if not isinstance(bases, tuple):
.... bases = bases,
.... cls = type("SpecialClass", bases, {})
.... return cls(*args)
....
a = create_special_object(list, [1,2,3])
a [1, 2, 3]
a.__class__
a.__class__.__nonzero__ = lambda self: False
bool(a) False
a.__nonzero__() False
a.append(4)
a [1, 2, 3, 4]
 
P

Pavel Panchekha

But you can give each object its own class and then put the special
methods in that class:

...   if not isinstance(bases, tuple):
...      bases = bases,
...   cls = type("SpecialClass", bases, {})
...   return cls(*args)
...>>> a = create_special_object(list, [1,2,3])
a [1, 2, 3]
a.__class__
a.__class__.__nonzero__ = lambda self: False
bool(a) False
a.__nonzero__() False
a.append(4)
a
[1, 2, 3, 4]

I think this is the solution I like best.
FYI this works as you expect if GeneralTypeOfObject is an old-style
class (i.e. does not inherit from object). If this feature is so more
important than all those that come with new-style classes, you have
control over the involved classes and don't care about Python 3.x
(where old-style classes are gone), you may choose to downgrade
GeneralTypeOfObject to old-style.

It is important, but new-style classes are important too. And I care
quite a bit about Python 3.x, thus the fact that I prefer the above
solution.
 
G

Gabriel Genellina

En Sat, 18 Apr 2009 19:39:24 -0300, Pavel Panchekha
But you can give each object its own class and then put the special
methods in that class:
def create_special_object(bases, *args):

...   if not isinstance(bases, tuple):
...      bases = bases,
...   cls = type("SpecialClass", bases, {})
...   return cls(*args)
...>>> a = create_special_object(list, [1,2,3])
a [1, 2, 3]
a.__class__

a.__class__.__nonzero__ = lambda self: False
bool(a)
False

I think this is the solution I like best.

You may want to implement some kind of cache: creating a new class for
every instance is expensive.
Also, note that those instances are not pickleable.
 
A

Aaron Brady

I've got an object which has a method, __nonzero__ The problem is, that
method is attached to that object not that class


What to do?

(1) Don't do that.

(2) If you *really* have to do that, you can tell the class to look at
the instance:

class GeneralTypeOfObject(object):
    def __nonzero__(self):
        try:
            return self.__dict__['__nonzero__']
        except KeyError:
            return something
snip

I think you need to call the return, unlike you would in
'__getattr__'.
return self.__dict__['__nonzero__']( )

You're free to use a different name for the method too.
return self.custom_bool( )

And you don't even have to implement an ABC. Er... /have/ to, that
is.
 
P

Pavel Panchekha

(1) Don't do that.
(2) If you *really* have to do that, you can tell the class to look at
the instance:
class GeneralTypeOfObject(object):
    def __nonzero__(self):
        try:
            return self.__dict__['__nonzero__']
        except KeyError:
            return something

snip

I think you need to call the return, unlike you would in
'__getattr__'.
             return self.__dict__['__nonzero__']( )

You're free to use a different name for the method too.
             return self.custom_bool( )

And you don't even have to implement an ABC.  Er... /have/ to, that
is.

I got it working. 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

No members online now.

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,189
Latest member
CryptoTaxSoftware

Latest Threads

Top