I have been trying to print a list using a __str__ method of a class, but before the list can be printed it has to be alphabetized through other methods coming before the __str__ method in the class. The provided methods for this are:
def returnName(self):
'''return the name of the player first last'''
return(self.first + self.last)
def returnReverseName(self):
'''return the name of the player as last, first'''
reverseName = (str(self.last), str(self.first))
return(reverseName)
def __eq__ (self, other):
'''determine if this persons name is the same as the other personsname'''
if self.returnReverseName == other.returnReverseName:
return True
else:
return False
def __lt__(self,other):
'''determine if this persons name is less than the other persons name alphabetically'''
if str(self.returnReverseName) < str(other.returnReverseName):
return True
else:
return False
def __gt__ (self, other):
'''determine if this persons name is greater than the other persons name alphabetically'''
if self.returnReverseName > other.returnReverseName:
return True
else:
return False
def __str__(self):
'''return a string of the persons name and their rating in a nice format'''
Name1 = str(self.first) + ' ' + str(self.last)
return ('%-20s %10.2f' % (Name1, self.rating))
where the name needs to be sorted by last name, first name but printed in first name last name order. The methods __lt__ and __gt__ are what should be ordering them when printed. The code I have so far to compile the list is:
basicList.sort()
for line in playerDict:
print(playerDict[line])
but this doesn't utilize the list, and is only printing the dictionary line by line in an unordered manner. Although it does refer to the methods it is just unable to alphabetize the names. How do I properly use the methods to get the dictionary printed in the right order? If I need to use the list of player objects, how do I get it to print using the __str__ method that would be printed post-alphabetization?
This code has many mistakes and oddities, but the first thing it does that could actually cause wrong results is that __eq__ and __gt__ are comparing two functions rather than two names. __lt__, for some reason, compares the string representations of those two functions. I don't know why it has different behavior than the other functions, but it's still wrong (just in a different way).
And I'm not surprised that the sorted list isn't used - you never use it.
basicList.sort() # sort the list
for line in playerDict: # that's not basicList
print(playerDict[line]) # still no basicList
Your class methods, if repaired, might look like this:
def returnName(self):
'''return the name of the player first last'''
return(self.first + ' ' + self.last)
def returnReverseName(self):
'''return the name of the player as last, first'''
return self.last, self.first
def __eq__ (self, other):
'''determine if this persons name is the same as the other personsname'''
return self.returnReverseName() == other.returnReverseName()
def __lt__(self,other):
'''determine if this persons name is less than the other persons name alphabetically'''
return self.returnReverseName() < other.returnReverseName()
def __gt__ (self, other):
'''determine if this persons name is greater than the other persons name alphabetically'''
return self.returnReverseName() > other.returnReverseName()
def __str__(self):
'''return a string of the persons name and their rating in a nice format'''
return '%-20s %10.2f' % (self.returnName(), self.rating)
But you probably don't need any of that. It looks like playerDict is a dictionary that holds some kind of keys attached to values that are Player objects (I assume that's the class name). Just iterate over those values and use a given sort key:
for entry in sorted(playerDict.values(), key=lambda x: x.last, x.first):
print(entry)
Related
I want to make a list of all the names that are given in a class this is the code right now I’m getting the error that is none type I feel like the append isn’t working
class Names:
def __init__(self,name):
self._name = name
self._list_name= []
def make_list(self,name):
self._list_name.append(name)
def __str__(self):
for i in self._list_name:
return i
You shouldn't return inside the loop. That will just return the first name in the list. And if you call __str__() before you call make_list(), there won't be anything in the list. The loop won't run and the method will return None, which is invalid for the __str__() method.
Instead, concatenate all the names into a string.
class Names:
def __init__(self,name):
self._name = name
self._list_name= []
def make_list(self,name):
self._list_name.append(name)
def __str__(self):
return ', '.join(self._list_name)
BTW, the name make_list doesn't really describe what that method does. It doesn't make a list, it adds to the list. Call it something like add_name().
I don't know how you plan on using this, but you might want to include the initial name in the list, so change
self._list_name = []
to
self._list_name = [name]
I want to print out data from a Class instance. I tried including data in str function, but this only works when value is at its default (an empty list). After I've added data to the list, I can only get memory objects printed. Can anyone help me troubleshoot why this is happening? I want to be able to print out objects to use in debugging.
class Student():
def __init__(self,name,year):
self.name=name
self.year=year
self.grades=[]
def add_grade(self, grade):
if type(grade)==Grade:
self.grades.append(grade)
else:
pass
def __str__(self):
return str(self.grades)
pieter=Student("Pieter Bruegel the Elder", 8)
print(pieter)
[] # Returns empty list of grades as it was initiated.
class Grade():
minimum_passing=65
def __init__(self,score):
self.score=score
def is_passing(self):
if self.score >= self.minimum_passing:
return True
else:
return False
pieter.add_grade(Grade(100))
pieter.add_grade(Grade(40))
print(pieter)
[<__main__.Grade object at 0x000002B354BF16A0>, <__main__.Grade object at 0x000002B354BF13A0>]
When you tell it to print 'pieter', you are telling it to attempt to print the object. What you need to do is add a print function in the class or use the specific variable in the object. Try adding something like this:
def print_object(self):
print("The name is: {self.name}")
print("The year is: {self.year}")
print("The grades are: {self.grades}")
If you have any questions, please feel free to reach out.
The list.__str__() method uses repr() to get the representation of the list elements. So this is showing the repr() of all the Grade objects in the grades list. Since Grade doesn't have a custom __repr__() method, you get the default representation.
You could define Grade.__repr__() to specify how you want grades to appear, or you could change the Student.__str__() method to get the scores from the Grade objects in the list.
class Student():
def __init__(self,name,year):
self.name=name
self.year=year
self.grades=[]
def add_grade(self, grade):
if isinstance(grade, Grade):
self.grades.append(grade)
else:
pass
def __str__(self):
return str([str(g) for g in self.grades])
The problem is that Grade itself doesn't have a neat representation as a string. Thus the str(self.grades) call can only print a list of abstract instance data. You could include a representation:
class Grade():
# the other stuff goes here...
def __repr__(self):
return f'Grade({self.score}'
Now print(pieter) prints:
[Grade(100), Grade(40)]
However, I wouldn't use the __str__() magic method to print the grades. It's clearer to define an explicit method such as print_grades() for this purpose. __str__() is normally used to output all the relevant information about a class (for debug purposes etc.) So in your case, it should return all the student info too, not just the grades.
In your add_grade method, you are inserting a Grade object into self.grades, and then when you print self.grades you get a list of Grade objects. Maybe you want to insert the score field from the Grade object into self.grades. That way you can print out the scores when you print out a student. Like so:
def add_grade(self, grade):
if type(grade) == Grade:
self.grades.append(grade.score)
Also, there is no need to write the else statement in the method, it will automatically return None if the argument passed to it is not a Grade object.
This question already has answers here:
What happens when objects in a Set are altered to match each other?
(4 answers)
Closed 11 months ago.
I have a simple custom object that represent custom tag, that user can attach to another object.
I want to store tags in a set, because I want to avoid duplicates and because order doesn't matter.
Each tag contain values "name" and "description". Later on, I might add another variables, but the key identifier for tag is "name".
I want to check whether tag is equal to other either by tag.name == other.name or against string tag == 'whatever'.
I want users to be able to edit tags including renaming them.
I have defined the object like this and everything worked as expected:
class Tag:
def __init__(self, name, description=""):
self.name = name
self.description = description
def __str__(self):
return self.name
def __repr__(self):
return self.name
def __eq__(self, other):
if isinstance(other, Tag):
return self.name == other.name
else:
return self.name == other
def __hash__(self):
return hash(self.name)
The problem appeared, when I tried to change the tag name:
blue_tag = Tag("blue")
tags = {blue_tag}
blue_tag in tags # returns True as expected
"blue" in tags # returns True as expected
blue_tag.name = "navy"
"navy" in tags # returns False. Why?
I don't understand why. The tag is correctly renamed, when I do print(tags). The id of bluetag object is also the same, hash of the name is also the same.
Everywhere, including Python official documentation, I found just basic info that in checks whether item is present in container and to define custom container, I need to define custom methods like __contains__ But I don't want to create custom set method.
The closest thing I found I found was a question here on SO:
Custom class object and "in" set operator
But it still didn't solve the problem.
The problem is that in changing a tag name attribute, you change its hash in the class above: and the hash of an object must not change after it is added to a set or as dictionary as a key.
The thing is that if two objects are "equal" they must have the same hash value - since you want your tags to be comparable by name, this implies that they can't have their name changed at all: if an object compares equal to another, their hash values must also be the equal: i.e. you can't simply add another immutable attribute to your class and base your hash value on that instead of the name.
The workaround I see in this case is to have a special "add_to_set" method on your Tag class; it would then track the sets it belongs to, and turn name into a property instance, so that whenever name is changed, it removes and re-adds the Tag itself from all sets it belongs to. The newly re-inserted tag would behave accordingly.
Making this work properly in parallel code would take somewhatmore work: as one could make use of the sets in another thread during the renaming - but if that is not a problem, then what is needed is:
class Tag:
def __init__(self, name, description=""):
self.sets = []
self.name = name
self.description = description
... # other methods as in your code
def __hash__(self):
return hash(self.name)
def add_to_set(self, set_):
self.sets.append(set_)
set_.add(self)
def remove_from_set(self, set_):
self.sets.remove(set_)
set_.remove(self)
#property
def name(self):
return self._name
#name.setter
def name(self, value):
# WARNING: this is as thread unsafe as it gets! Do not use this class
# in multi-threaded code. (async is ok)
try:
for set_ in self.sets:
set_.remove(self)
self._name = value
finally:
for set_ in self.sets:
set_.add(self)
And now:
In [17]: a = Tag("blue")
In [18]: b = set()
In [19]: a.add_to_set(b)
In [20]: a in b
Out[20]: True
In [21]: b
Out[21]: {blue}
In [22]: a.name = "mauve"
In [23]: b
Out[23]: {mauve}
In [24]: a in b
Out[24]: True
It is possible to specialize a set class that would automatically call the add_to_set and remove_from_set methods for you as well, but this is likely enough.
New to python and this might be a silly question, but how does one properly implement the repr method?
I wrote a quick little program to simulate a game of cards but I don't know what to write for the repr method. The repr method for the Card class was pretty straight forward, but I don't know what to do for the DeckOfCards class Here's my code:
import random
class Card:
'''Create a single card, by id number'''
# Class variables, created once for the class
suits = [ '\u2660', '\u2661', '\u2662', '\u2663' ]
ranks = [ 'A','2','3','4','5','6','7','8','9','10','J','Q','K' ]
def __init__(self, n=0):
# instance variables for _num, _rank, _suit, _value
if 0 <= n < 52:
self._num = n
self._rank = Card.ranks[n%13] # note referencing class vars
self._suit = Card.suits[n//13]
self._value = n%13 + 1
if self._rank == 'A':
self._value = 14
else: # invalid card indicators
self._rank = 'x'
self._suit = 'x'
self._value = -1
def __repr__(self):
return self._rank + self._suit
def __lt__(self,other):
return self._value < other._value
def __le__(self,other):
return self._value <= other._value
def __eq__(self,other):
return self._value == other._value
class DeckOfCards:
'''A Deck is a collection of cards'''
def __init__(self):
self._deck = [ Card(i) for i in range(52) ]
def __repr__(self):
return 'Deck : ', self._deck
def shuffle(self):
return random.shuffle(self._deck)
def deal_a_card(self, i=-1):
#that way player can choose where to draw from
return self._deck.pop(i)
def cards_left(self,count):
return len(self._deck)
new_deck = DeckOfCards()
Also, feel free to comment on anything you'd like, whether it be a design flaw or redundancy in code, literally anything. Thanks in advance!
You should return a string type, for example in Deck:
def __repr__(self):
...
return 'Deck : '+str(self._deck)
__repr__ ideally could return the representation of the object that you would use to create this instance.
From repr():
For many types, this function makes an attempt to return a string that would yield an object with the same value when passed to eval(), otherwise the representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object.
First, It should be noted that you don't have to implement the __repr__ method. Python provides a somewhat reasonable default (it'll at least tell you the type).
If you want to implement __repr__, the "rule of thumb" is that where it makes sense, you should provide enough information about the object that a user could reconstruct it. In your case, there doesn't seem to be any real difference from one deck to another, so
def __repr__(self):
return 'Deck()'
might be a reasonable return value. This doesn't get the state right (after shuffling), but you don't provide an interface for constructing a deck in a particular state. If you did, it might look like:
def __repr__(self):
return 'Deck(%s)' % self._deck
Here, I am attempting to mock up a social media profile as a class "Profile", in which you have name, a group of friends, and the ability to add and remove friends. There is a method that I would like to make, that when invoked, will print the list of friends in alphabetical order.
The issue: I get a warning that I cannot sort an unsortable type. Python is seeing my instance variable as a "Profile object", rather than a list that I can sort and print.
Here is my code:
class Profile(object):
"""
Represent a person's social profile
Argument:
name (string): a person's name - assumed to uniquely identify a person
Attributes:
name (string): a person's name - assumed to uniquely identify a person
statuses (list): a list containing a person's statuses - initialized to []
friends (set): set of friends for the given person.
it is the set of profile objects representing these friends.
"""
def __init__(self, name):
self.name = name
self.friends = set()
self.statuses = []
def __str__(self):
return self.name + " is " + self.get_last_status()
def update_status(self, status):
self.statuses.append(status)
return self
def get_last_status(self):
if len(self.statuses) == 0:
return "None"
else:
return self.statuses[-1]
def add_friend(self, friend_profile):
self.friends.add(friend_profile)
friend_profile.friends.add(self)
return self
def get_friends(self):
if len(self.friends) == 0:
return "None"
else:
friends_lst = list(self.friends)
return sorted(friends_lst)
After I fill out a list of friends (from a test module) and invoke the get_friends method, python tells me:
File "/home/tjm/Documents/CS021/social.py", line 84, in get_friends
return sorted(friends_lst)
TypeError: unorderable types: Profile() < Profile()
Why can't I simply typecast the object to get it in list form? What should I be doing instead so that get_friends will return an alphabetically sorted list of friends?
Sorting algorithms look for the existence of __eq__, __ne__, __lt__, __le__, __gt__,__ge__ methods in the class definition to compare instances created from them. You need to override those methods in order to tweak their behaviors.
For performance reasons, I'd recommend you to define some integer property for your class like id and use it for comparing instead of name which has string comparison overhead.
class Profile(object):
def __eq__(self, profile):
return self.id == profile.id # I made it up the id property.
def __lt__(self, profile):
return self.id < profile.id
def __hash__(self):
return hash(self.id)
...
Alternatively, you can pass a key function to sort algorithm if you don't want to bother yourself overriding those methods:
>>> friend_list = [<Profile: id=120>, <Profile: id=121>, <Profile: id=115>]
>>> friend_list.sort(key=lambda p: p.id, reverse=True)
Using operator.attrgetter;
>>> import operator
>>> new_friend_list = sorted(friend_list, key=operator.attrgetter('id'))
I think i'll take a crack at this. first, here's teh codes:
from collections import namedtuple
class Profile(namedtuple("Profile", "name")):
def __init__(self, name):
# don't set self.name, it's already set!
self.friends = set({})
self.statuses = list([])
# ... and all the rest the same. Only the base class changes.
what we've done here is to create a class with the shape of a tuple. As such, it's orderable, hashable, and all of the things. You could even drop your __str__() method, namedtuple provides a nice one.