So as part of problem 17.6 in "Think Like a Computer Scientist", I've written a class called Kangaroo:
class Kangaroo(object):
def __init__(self, pouch_contents = []):
self.pouch_contents = pouch_contents
def __str__(self):
'''
>>> kanga = Kangaroo()
>>> kanga.put_in_pouch('olfactory')
>>> kanga.put_in_pouch(7)
>>> kanga.put_in_pouch(8)
>>> kanga.put_in_pouch(9)
>>> print kanga
"In kanga's pouch there is: ['olfactory', 7, 8, 9]"
'''
return "In %s's pouch there is: %s" % (object.__str__(self), self.pouch_contents)
def开发者_C百科 put_in_pouch(self, other):
'''
>>> kanga = Kangaroo()
>>> kanga.put_in_pouch('olfactory')
>>> kanga.put_in_pouch(7)
>>> kanga.put_in_pouch(8)
>>> kanga.put_in_pouch(9)
>>> kanga.pouch_contents
['olfactory', 7, 8, 9]
'''
self.pouch_contents.append(other)
What's driving me nuts is that I'd like to be able to write a string method that would pass the unit test underneath __str__
as written. What I'm getting now instead is:
In <__main__.Kangaroo object at 0x4dd870>'s pouch there is: ['olfactory', 7, 8, 9]
Bascially, what I'm wondering if there is some function that I can perform on kanga = Kangaroo such that the output of the function is those 5 characters, i.e. function(kanga) -> "kanga".
Any ideas?
Edit:
Reading the first answer has made me realize that there is a more concise way to ask my original question. Is there a way to rewrite __init__
such that the following code is valid as written?
>>> somename = Kangaroo()
>>> somename.name
'somename'
To put your request into perspective, please explain what name you would like attached to the object created by this code:
marsupials = []
marsupials.append(Kangaroo())
This classic essay by the effbot gives an excellent explanation.
To answer the revised question in your edit: No.
Now that you've come clean in a comment and said that the whole purpose of this naming exercise was to distinguish between objects for debugging purposes associated with your mutable default argument:
In CPython implementations of Python at least, at any given time, all existing objects have a unique ID, which may be obtained by id(obj)
. This may be sufficient for your debugging purposes. Note that if an object is deleted, that ID (which is a memory address) can be re-used by a subsequently created object.
I wasn't going to post this but if you only want this for debugging then here you go:
import sys
class Kangaroo(object):
def __str__(self):
flocals = sys._getframe(1).f_locals
for ident in flocals:
if flocals[ident] is self:
name = ident
break
else:
name = 'roo'
return "in {0}'s pouch, there is {1}".format(name, self.pouch_contents)
kang = Kangaroo()
print kang
This is dependent on CPython (AFAIK) and isn't suitable for production code. It wont work if the instance is in any sort of container and may fail for any reason at any time. It should do the trick for you though.
It works by getting the f_locals
dictionary out of the stack frame that represents the namespace where print kang
is called. The keys of f_locals
are the names of the variables in the frame so we just loop through it and test if each entry is self
. If so, we break
. If break
is not executed, then we didn't find an entry and the loops else
clause assigns the value 'roo' as requested.
If you want to get it out of a container of some sort, you need to extend this to look through any containers in f_locals
. You could either return the key if it's a dictlike container or the index if it's something like a tuple or list.
class Kangaroo(object):
def __init__(self, pouch_contents=None, name='roo'):
if pouch_contents is None:
self.pouch_contents = [] # this isn't shared with all other instances
else:
self.pouch_contents = pouch_contents
self.name = name
...
kanga = Kangaroo(name='kanga')
Note that it's good style not to put spaces around =
in the arguments
What you want is basically impossible in Python, even with the suggested "hacks". For example, what would the following code print?
>>> kanga1 = kanga2 = kanga3 = Kangaroo()
>>> kanga2.name
???
>>> kanga3.name
???
or
>>> l = [Kangaroo()]
>>> l[0].name
???
If you want "named" objects, just supply a name to your object
def __init__(self, name):
self.name = name
More explicit (which we like with Python) and consistent in all cases. Sure you can do something like
>>> foo = Kangaroo("bar")
>>> foo.name
'bar'
but foo will be just one of the possibly many labels the instance has. The name is explicit and permanent. You can even enforce unique naming if you want (while you can reuse a variable as much as you want for different objects)
I hadn't seen aaronasterling's hackish answer when I started working on this, but in any case here's a hackish answer of my own:
class Kangaroo(object):
def __init__(self, pouch_contents = ()):
self.pouch_contents = list(pouch_contents)
def __str__(self):
if not hasattr(self, 'name'):
for k, v in globals().iteritems():
if id(v) == id(self):
self.name = k
break
else:
self.name = 'roo'
return "In %s's pouch there is: %s" % (self.name, self.pouch_contents)
kanga = Kangaroo()
print kanga
You can break this by looking at it funny (it works as written, but it will fail as part of a doctest), but it works. I'm more concerned with what's possible than with what's practical at this point in my learning experience, so I'm glad to see that there are at least two different ways to do a thing I figured should be possible to do. Even if they're bad ways.
精彩评论