"Aliasing" an object's __str__ to a different method

J

Jeffrey E. Forcier

I am attempting to write a class whose string representation changes
in response to external stimuli. While that effect is obviously
possible via other means, I attempted this method first and was
surprised when it didn't work, so I now want to know why :)

Given the following class definition:

class MyClass(object):

def Edit(self):
return "I, %s, am being edited" % (self)

def View(self):
return "I, %s, am being viewed" % (self)

def setEdit(self):
self.__str__ = self.__repr__ = self.Edit

def setView(self):
self.__str__ = self.__repr__ = self.View


....I would expect the following behavior:

In [130]: testObject = MyClass()
In [131]: testObject.setEdit()
In [132]: str(testObject)
Out[132]: 'I, <__main__.MyClass object at 0x511270>, am being edited'


Unfortunately, this is what happens instead:

In [130]: testObject = MyClass()
In [131]: testObject.setEdit()
In [132]: str(testObject)
Out[132]: '<__main__.MyClass object at 0x511270>'


In other words, str() is _NOT_ apparently calling <object>.__str__ as
it's supposed to! However, calling __str__ directly (which, yes,
you're not supposed to do) does work as expected:

In [133]: testObject.__str__()
Out[133]: 'I, <__main__.MyClass object at 0x511270>, am being edited'


Now, my understanding is that functions/methods are first-order
objects, and can be pointed to via reference, and that appears to be
true:

In [135]: def func1():
.....: print "I'm func1"
.....:
In [136]: def func2():
.....: print "I'm func2"
.....:
In [137]: func2 = func1
In [138]: func2()
I'm func1


However, expressions such as self.__str__ = <some function or method>
aren't working for me, as above. Why not?

Thanks for any responses,
Jeff

--
Jeffrey E. Forcier
Junior Developer, Research and Development
Stroz Friedberg, LLC
15 Maiden Lane, 12th Floor
New York, NY 10038
[main]212-981-6540 [direct]212-981-6546
http://www.strozllc.com

This message is for the named person's use only. It may contain
confidential, proprietary or legally privileged information. No right to
confidential or privileged treatment of this message is waived or lost
by any error in transmission. If you have received this message in
error, please immediately notify the sender by e-mail or by telephone at
212.981.6540, delete the message and all copies from your system and
destroy any hard copies. You must not, directly or indirectly, use,
disclose, distribute, print or copy any part of this message if you are
not the intended recipient.
 
M

Michael Hoffman

Jeffrey said:
In other words, str() is _NOT_ apparently calling <object>.__str__ as
it's supposed to! However, calling __str__ directly (which, yes, you're
not supposed to do) does work as expected:

I'm too tired and/or lazy to check, but I believe str() is calling
MyClass.__str__(testObject) as it is supposed to rather than
testObject.__str__() as you say it is supposed to. ;-)

Someone else or Google can probably enlighten you on the reason for this
better than I. I think this is something that is not explained very well
in the docs, which do say that a class implements special method names,
but doesn't make it very clear that an object can't always override them.

If you redirect to a second method you can overwrite on the instance, I
think you will get the results you want.
 
J

Jeffrey E. Forcier

I'm too tired and/or lazy to check, but I believe str() is calling
MyClass.__str__(testObject) as it is supposed to rather than
testObject.__str__() as you say it is supposed to. ;-)

Someone else or Google can probably enlighten you on the reason for
this
better than I. I think this is something that is not explained very
well
in the docs, which do say that a class implements special method
names,
but doesn't make it very clear that an object can't always override
them.

If you redirect to a second method you can overwrite on the
instance, I
think you will get the results you want.

So in other words, __str__() and its ilk are class methods instead of
instance methods? The documentation appears to contradict this:

(http://python.org/doc/2.4/ref/specialnames.html)
"For instance, if a class defines a method named __getitem__(), and x
is an instance of this class, then x is equivalent to x.__getitem__
(i)."


However, you appear to be correct, and the docs appear to be confused:

class MyClass(object):

def edit(self):
return "I'm in edit mode"

def setToEdit(self):
MyClass.__str__ = self.edit

In [3]: test = MyClass()
In [4]: str(test)
Out[4]: '<__main__.MyClass object at 0x2d21d0>'
In [5]: test.setToEdit()
In [6]: str(test)
Out[6]: "I'm in edit mode"


Of course, this doesn't quite do what I had in mind, and in fact you
could say it's kinda sloppy, binding an ostensibly static class
method to an instance method:

In [7]: repr(MyClass.__str__)
Out[7]: '<bound method MyClass.edit of <__main__.MyClass object at
0x2d21d0>>'


Either way, guess I will have to take another route. Your suggestion
is what I'd had in mind, and does the job pretty well. I'm still
attempting to figure out the best approach to my overall problem,
however, so who knows where I will end up =)

Thanks for the response! If you or anyone else can shed some insight
on *why* this is the way it is (and why/if the docs are in fact
incorrect) I'd appreciate further replies. More knowledge == better.

Regards,
Jeff

--
Jeffrey E. Forcier
Junior Developer, Research and Development
Stroz Friedberg, LLC
15 Maiden Lane, 12th Floor
New York, NY 10038
[main]212-981-6540 [direct]212-981-6546
http://www.strozllc.com

This message is for the named person's use only. It may contain
confidential, proprietary or legally privileged information. No right to
confidential or privileged treatment of this message is waived or lost
by any error in transmission. If you have received this message in
error, please immediately notify the sender by e-mail or by telephone at
212.981.6540, delete the message and all copies from your system and
destroy any hard copies. You must not, directly or indirectly, use,
disclose, distribute, print or copy any part of this message if you are
not the intended recipient.
 
N

ncf

In trying to develop a protocol for a current app I'm working on, I was
using classes which inherited from object for my core packet, and using
str(Message) to convert it to an encoded packet. However, I found that
this did not work, and it drove me insane, so in a test file, I wrote
the following code:

class Whatever:
''' Supposed to be inheritable '''
def __init__(self):
self.__str__ = self._encode # Dynamically set the __str__ from
superclass(?)

Well, suffice to say, having the class not inherit from object solved
my problem, as I suspect it may solve yours. ;)

I havn't a clue why it acts that way, however, I hope knowledge of my
past experiances helps you also.

Have a GREAT day :)

-Wes
 
C

Christopher Subich

ncf said:
Well, suffice to say, having the class not inherit from object solved
my problem, as I suspect it may solve yours. ;)

Actually, I did a bit of experimenting. If the __str__ reassignment
worked as intended, it would just cause an infinite recursion.

To paste the class definition again:
class MyClass(object):

def Edit(self):
return "I, %s, am being edited" % (self)

def View(self):
return "I, %s, am being viewed" % (self)

def setEdit(self):
self.__str__ = self.__repr__ = self.Edit

def setView(self):
self.__str__ = self.__repr__ = self.View

Notice the % (self) in Edit and View -- those recursively call
str(self), which causes infinite recursion.

In the spirit of getting the class working, though, the class-method
behavior of __str__ for new-style classes can be fixed with an extremely
ugly hack:
class MyClass(object):
def __init__(self):
self.__str__ = lambda : object.__str__(self)
def Edit(self):
return "I, %s, am being edited"

def View(self):
return "I, %s, am being viewed"

def setEdit(self):
self.__str__ = self.__repr__ = self.Edit

def setView(self):
self.__str__ = self.__repr__ = self.View
def __str__(self):
return self.__str__()

(Notice that I've removed the string substitution in Edit and View.
This also does not change the __repr__ method; it also acts as a class
method. But that's also easy enough to change in the same way.)

I also would be interested in knowing why new-style classes treat
__str__ as a class method.
 
P

Paolino

Little less ugly:
In [12]:class A(object):
....: def __str__(self):return self.__str__()
....: def str(self):return 'ciao'
....: def setStr(self):self.__str__=self.str
....:

In [13]:a=A()

In [14]:a.setStr()

In [15]:str(a)
Out[15]:'ciao'

The point is str(ob) builtin looks like calling
ob.__class__.__str__(ob).Prolly Python-dev is the good place to ask about.







___________________________________
Yahoo! Mail: gratis 1GB per i messaggi e allegati da 10MB
http://mail.yahoo.it
 
S

Scott David Daniels

Jeffrey said:
...
However, you appear to be correct, and the docs appear to be confused:
class MyClass(object):
def edit(self):
return "I'm in edit mode"
def setEdit(self):
MyClass.__str__ = self.edit
...
Either way, guess I will have to take another route. Your suggestion is
what I'd had in mind, and does the job pretty well. I'm still
attempting to figure out the best approach to my overall problem,
however, so who knows where I will end up =)

Here is one more standard way to do this:

class MyClass(object):
... # Common behavior goes here

def setEdit(self):
self.__class__ = MyEditClass

def setView(self):
self.__class__ = MyViewClass


class MyEditClass(MyClass):
def __repr__(self):
return "I, %s, am being edited" % super(
MyEditClass, self).__repr__()

class MyViewClass(MyClass):
def __repr__(self):
return "I, %s, am being viewed" % super(
MyViewClass, self).__repr__()


Be a little careful about the structure of the subclasses (MyViewClass
and MyEditClass) since they can wink into and out of existence, and
all will go well. Plus, you can easily override base behavior in the
subclasses differentially.

Note: for this example, you could also define the methods like:

class MyEditClass(MyClass):
def __repr__(self):
return "I, %s, am being edited" % MyClass.__repr__(self)

When to use super rather than direct access to the superclass is an
involved discussion.

--Scott David Daniels
(e-mail address removed)
 
C

Christopher Subich

Paolino said:
Little less ugly:
In [12]:class A(object):
....: def __str__(self):return self.__str__()
....: def str(self):return 'ciao'
....: def setStr(self):self.__str__=self.str
....:

In [13]:a=A()

In [14]:a.setStr()

In [15]:str(a)
Out[15]:'ciao'

Not quite bug-free, by my eye that'll infintely recur if you call str(A()).
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top