Complete program:
class Score:
def __init__(self, name_, score_):
self.name = name_
self.score = score_
def __str__(self):
return "Name = %s, score = %d" % (self.name, self.score)
def __cmp__(self, other):
print "in __cmp__"
return self.score >= other.score
name = ""
score = 0
# End class Score
filename = "../foo.txt";
try:
infile = open(filename, "r")
except IOError, (errno, strerror):
print "IOError caught when attempting to open file %s. errno = %d,
strerror = %s" % (filename, errno, strerror)
exit(1)
lines = infile.readlines()
infile.close()
lines = [l.strip() for l in lines] # Strip away trailing newlines.
scores = []
for a_line in lines:
splitstuff = a_line.split()
scores.append(Score(splitstuff[0], int(splitstuff[1])))
scores.sort()
for a_score in scores:
print a_score
Test file contents:
Michael 11
Hanna 1337
Lena 99
Output:
in __cmp__
in __cmp__
Name = Michael, score = 11
Name = Hanna, score = 1337
Name = Lena, score = 99
What am I doing wrong here?
- Eric (WP)
I solved it, I rewrote __cmp__ to:
def __cmp__(self, other):
if self.score == other.score:
return cmp(self.name, other.name)
else:
return cmp(other.score, self.score)
This sorts so that score goes from highest to lowest. If two person have
the same score, I sort on names in alphabetical order. The following
text file:
Michael 11
Hanna 1337
Lena 99
Anna 99
yields (just as I want):
Name = Hanna, score = 1337
Name = Anna, score = 99
Name = Lena, score = 99
Name = Michael, score = 11
- Eric (WP)
From http://docs.python.org/ref/customization.html:
__cmp__( self, other)
Called by comparison operations if rich comparison (see above) is
not defined. Should return a negative integer if self < other, zero if
self == other, a positive integer if self > other.
You're not handling all the comparison cases, just assumping __cmp__ is
being called for >=, when it's not.
You can fix your __cmp__ function, but it's probably easier to
understand if you overload specific comparison functions: __lt__,
__gt__, etc.
Hi,
> Hello, here are some new things I've problems with. I've made a program
...
> def __cmp__(self, other):
> print "in __cmp__"
> return self.score >= other.score
Check this out: http://docs.python.org/ref/customization.html
Definition of this method should rather look like this:
==========================================
def __cmp__(self, other):
print "in __cmp__"
if self.score == other.score:
return 0
elif self.score > other.score:
return 1
return -1
==========================================
Should work fine now.
--
Regards,
Wojtek Walczak,
http://www.stud.umk.pl/~wojtekwa/
> I solved it, I rewrote __cmp__ to:
> def __cmp__(self, other):
> if self.score == other.score:
> return cmp(self.name, other.name)
> else:
> return cmp(other.score, self.score)
You can simplify that to
def __cmp__(self, other):
return cmp(other.score, self.score) or cmp(self.name, other.name)
Alternatively you can define a key function
def descending_score(s):
return -s.score, s.name
and then use it:
scores.sort(key=descending_score)
Because list.sort() is stable sorting twice will also work:
scores.sort(key=lambda s: s.name)
scores.sort(key=lambda s: s.score, reverse=True)
Peter
> class Score:
> def __init__(self, name_, score_):
> self.name = name_
> self.score = score_
These trailing underscores look like a habit from another language. They
are unneeded in Python; you can write:
class Score:
def __init__(self, name, score):
self.name = name
self.score = score
That's an advantage of the explicit self: no ambiguity between local
variables and attributes.
> try:
> infile = open(filename, "r")
> except IOError, (errno, strerror):
> print "IOError caught when attempting to open file %s. errno = %d,
> strerror = %s" % (filename, errno, strerror)
> exit(1)
This try/except block is unneeded too: what you do with the exception is
more or less the same as the regular behaviour (print an error message and
exit), except your error message is far less informative than the default
one, and printed to stdout instead of stderr. I would remove the entire
try/except block and just write:
infile = open(filename, "r")
HTH
--
python -c "print ''.join([chr(154 - ord(c)) for c in
'U(17zX(%,5.zmz5(17l8(%,5.Z*(93-965$l7+-'])"
You fixed this, so I'll just comment on the rest of the code...
(snip)
> Complete program:
> class Score:
Unless you have a compelling reason (mostly: compat with ages old Python
versions), better to use new-style classes:
class Score(object):
> def __init__(self, name_, score_):
> self.name = name_
> self.score = score_
cf Eric Brunel's comments here about the trailing underscores.
> def __str__(self):
> return "Name = %s, score = %d" % (self.name, self.score)
>
(snip)
> name = ""
> score = 0
Do you have any reason to have these as class attributes too ?
> # End class Score
>
> filename = "../foo.txt";
>
> try:
> infile = open(filename, "r")
> except IOError, (errno, strerror):
> print "IOError caught when attempting to open file %s. errno = %d,
> strerror = %s" % (filename, errno, strerror)
> exit(1)
cf Eric Brunel's comment wrt/ why this try/except clause is worse than
useless here.
> lines = infile.readlines()
File objects are their own iterators. You don't need to read the whole
file in memory.
> infile.close()
> lines = [l.strip() for l in lines] # Strip away trailing newlines.
>
> scores = []
>
> for a_line in lines:
> splitstuff = a_line.split()
>
> scores.append(Score(splitstuff[0], int(splitstuff[1])))
scores = []
mk_score = lambda name, score : Score(name, int(score))
infile = open(filename)
for line in infile:
line = line.strip()
if not line:
continue
scores.append(mk_score(*line.split()))
infile.close()
As a side note : I understand that this is a learning exercice, but in
real life, if that's all there is to your Score class, it's not really
useful - I'd personnally use a much simpler representation, like a list
of score / name pairs (which is easy to sort, reverse, update, turn into
a name=>scores or scores=>names dict, etc).
My 2 cents.