overload the > operator (__gt__) - python

I want my Book class to overload the > operator __gt__. So, I can use it in my BookCollection to find the right order.
I used > in InsertBook(). But it is not working! How do I fix it? I want it to be ordered by
the Book’s author (alphabetical / lexicographical order)
the Book’s year of publication
the Book’s title (alphabetical / lexicographical order).
This is my current code for the class Book.py:
class Book:
def __init__(self, title='', author='',year=None):
self.title = title
self.author = author
self.year = year
def setTitle(self, title):
self.title = title
def setAuthor(self, author):
self.author = author
def setYear(self, year):
self.year = year
def getTitle(self):
return self.title
def getAuthor(self):
return self.author
def getYear(self):
return self.year
def __gt__(self,item):
if (0,1,0) > (0,0,0):
return self.author > item.author
else:
return False
if (1,0,0) > (0,0,0):
return self.title > item.title
else:
return False
if (0,0,1) > (0,0,0):
return self.year > item.year
else:
return False
def getBookDetails(self):
return "Title: {}, Author: {}, Year: {}".format(self.title, self.author, self.year)
class BookCollection():
def __init__(self):
self.head = None
def isEmpty(self):
return self.head == None
def insertBook(self, book):
current = self.head
previous = None
stop = False
while current != None and not stop:
if current.getData() > book:
stop = True
else:
previous = current
current = current.getNext()
temp = BookCollectionNode(book)
if previous == None:
temp.setNext(self.head)
self.head = temp
else:
temp.setNext(current)
previous.setNext(temp)
def getNumberOfBooks(self):
temp = self.head
count = 0
while temp != None:
count = count + 1
temp = temp.getNext()
return count
def getAllBooksInCollection(self):
current = self.head
output = ""
while current != None:
output += str(current.getData()) + " "
current = current.getNext()
output = output[:len(output)-1] + "\n"
return output
def getBooksByAuthor(self,item):
current = self.head
found = False
stop = False
while current != None and not found and not stop:
if current.getData() == item:
found = True
else:
if current.getData() > item:
stop = True
else:
current = current.getNext()
return found

I used > in InsertBook(). But it is not working! How do I fix it? I want it to be ordered by
Your overload of the method makes no sense, why are you comparing completely static tuple values? That's always going to do the same thing, so your entire override is really just return self.author > item.author
Overriding __gt__ alone is a bad idea: you really need to override them all or you'll get incoherent result. The functools.total_ordering class decorator allows overriding just __eq__ and one ordering operator, you probably want to use that.
I want it to be ordered by
the Book’s author (alphabetical / lexicographical order)
the Book’s year of publication
the Book’s title (alphabetical / lexicographical order).
Then compare that? I'd recommend a key method or property for simplicity e.g.
#total_ordering
class Book:
#property
def _key(self):
return (self.author, self.year, self.title)
def __eq__(self, other):
return self._key == other._key
def __gt__(self, other):
return self._key > other._key
should do the trick and order your records by ascending author, ascending year of publication, and ascending title
That aside,
Python is not Java
All your trivial getters and setters are useless and redundant, just remove them outright. And getBookDetails should just be the implementation of your __str__.
BookCollection is similarly concerning, isEmpty is not a thing in Python, instead collections implement __len__ (and optionally __bool__ for efficiency). And the entire thing is a mess, why doesn't BookCollection just contain a list of books (or some other collection from the stdlib)? What's the purpose of making it some sort of ad-hoc half-assed linked list?
And it shoudl provide (or use) an iterator, you're implementing the same iteration scheme 3 different times for no reason.
Your naming scheme is also incorrect, Python normally uses snake_case for methods, and properties, and fields.

Related

Python list of classes , index() not working

Not sure what i'm doing wrong here. I have this class:
class Node:
'''
Class to contain the lspid seq and all data.
'''
def __init__(self, name,pseudonode,fragment,seq_no,data):
self.name = name
self.data = {}
self.pseudonode = pseudonode
self.seq_no = seq_no
self.fragment = fragment
def __unicode__(self):
full_name = ('%s-%d-%d') %(self.name,self.pseudonode,self.fragment)
return str(full_name)
def __cmp__(self, other):
if self.name > other.name:
return 1
elif self.name < other.name:
return -1
return 0
def __repr__(self):
full_name = ('%s-%d-%d') %(self.name,self.pseudonode,self.fragment)
#print 'iside Node full_name: {} \n\n\n ------'.format(full_name)
return str(full_name)
and putting some entries in a list :
nodes = []
node = Node('0000.0000.0001',0,0,100,{})
nodes.append(node)
>>> nodes
[0000.0000.0001-0-0]
node = Node('0000.0000.0001',1,0,100,{})
nodes.append(node)
>>> nodes
[0000.0000.0001-0-0, 0000.0000.0001-1-0]
i'm trying to get the index of a node in list nodes[]
>>> node
0000.0000.0001-1-0
>>> nodes.index(node)
0
0 is not what i was expecting. Not sure why this is happening.
edit
i'm after getting the index of the list where '0000.0000.0001-1-0' is.
The index function, when used on a container, relies on its element's __cmp__ function to return the index of the first element that it thinks is equal to the input-object. You probably know as much, since you implemented it for the node. But what you are expecting is that __cmp__ considers not only the name, but also the pseudonode and the fragment, right?
A straight-forward approach would be to consider them a tuple, which performs a comparison of elements from left to right, until the first inequality was found:
def __cmp__(self, other):
self_tuple = (self.name, self.pseudonode, self.fragment)
other_tuple = (other.name, other.pseudonode, other.fragment)
if self_tuple > other_tuple:
return 1
elif self_tuple < other_tuple:
return -1
return 0
If you want another order, you can use the tuples-ordering to define it.

SyntaxError: non-default argument follows default argument; __init__ related [duplicate]

This question already has answers here:
Why can't non-default arguments follow default arguments?
(4 answers)
Closed 5 years ago.
This is my last resort. I've searched everywhere, but it's always pertaining to it being re-written by another function or something. I assure you there is no other function that uses this init thing. I'm a beginner.
The error is that I get this: SyntaxError: non-default argument follows default argument. It points towards __init__([here]self, ...etcetc).
class book:
def __init__(self, bookId, nextBook=None, name, author, ageGroup):
self.bookId = bookId
self.nextBook = nextBook
self.name = str(name)
self.author = str(author)
self.ageGroup = str(ageGroup)
def getInfo(self):
return self.bookdId
def setInfo(self, bookId, bookName, authorName, ageGroup):
self.bookId = bookId
self.name = bookName
self.author = authorName
self.ageGroup = ageGroup
def getNextBook(self):
return self.nextBook
def setNextBook(self,val):
self.nextBook = val
class bookCollection:
def __init__(self,head = None):
self.head = head
self.size = 0
def getSize(self):
return self.size
def addBookToFront(self,bookId):
newBook = book(bookId,self.head)
self.head = newBook
self.size+=1
return True
def listAllBooks(self):
curr = self.head
while curr:
print(curr.data)
print(curr.name)
curr = curr.getNextBook()
def deleteBook(self,value):
prev = None
curr = self.head
while curr:
if curr.getInfo() == value:
if prev:
prev.setNextBook(curr.getNextBook())
else:
self.head = curr.getNextBook()
return True
prev = curr
curr = curr.getNextBook()
return False
def addBookAtPosition(self, newBookId, position):
counter = 1
if position == 0:
newBookId.setNextBook(self.head)
self.head = newBookId
else:
book = self.head
while book.getNextBook() is not None:
if counter == position:
newBookId.setNextBook(book.getNextBook())
book.setNextBook(newBookId)
book = book.getNextBook()
counter = counter + 1
def removeBookAtPosition(self, removedBookId, position):
counter = 1
if position == 0:
self.head = removedBookId.getNextBook()
else:
book = self.head
while book.getNextBook() is not None:
if counter == position - 1:
book.setNextBook(removedBookId.getNextBook())
book = book.getNextBook()
counter = counter + 1
#for reference: bookId, bookName, authorName, ageGroup
Boy = book(1, Boy, Roald-Dahl, Teens)
It's complaining about this line:
def __init__(self, bookId, nextBook=None, name, author, ageGroup):
Argument nextBook has a default value, namely None.
But arguments name, author and ageGroup don't have default values.
That is prohibited by the interpreter, or, more exact, by the parser part of the interpreter.
Either don't give nextBook a default value, or give default values also for name, author and ageGroup. If you want a default value only for nextBook, make it the last parameter, so after name, author and ageGroup.

Why isn't this instance being removed from my list of objects?

So I have created 2 classes called Invitation and Response. Then, I created a class called Event. Within the Event class, I have to create function that looks at the current list of invites and list of responses and counts how many invites have no response.
The invites and the responses both have a "name" attribute that I use in my get_pending() method.
I have a class instance called "e" that looks as follows:
e = Event("graduation",[Invitation("A",5),Invitation("B",10),Invitation("C",5),Invitation("D",7)], [Response("A",True,5),Response("B",True,6),Response("C",False,0),Response("D",True,1)])
I then print the class method get_pending:
print(e.count_pending())
Here is my code with the get_pending() method:
class Invitation:
def __init__(self, name, num_invited):
self.name = name #str
self.num_invited = num_invited #positive int
def __str__(self):
return ("Invitation('%s', %i)" % (self.name, self.num_invited))
def __repr__(self):
return str(self)
def __eq__(self, other):
if self.name == other.name and self.num_invited == other.num_invited:
return True
def __lt__(self, other):
if self.name < other.name:
return True
elif self.name == other.name:
if self.num_invited < other.num_invited:
return True
class Response:
def __init__(self, name, ans, num_attending):
self.name = name #str
self.ans = ans #bool - T/F
self.num_attending = num_attending #zero or more people
def __str__(self):
return ("Response('%s', %r, %i)" % (self.name, self.ans, self.num_attending))
def __repr__(self):
return str(self)
def __eq__(self, other):
if self.name == other.name and self.ans == other.ans and self.num_attending == other.num_attending:
return True
def __lt__(self, other):
if self.name < other.name:
return True
elif self.name == other.name:
if self.ans < other.ans:
return True
elif self.ans == other.ans:
if self.num_attending < other.num_attending:
return True
class Event:
def __init__(self, title, invites=None, responses=None):
self.title = title #str
self.invites = invites #list
self.responses = responses #list
if self.invites == None:
self.invites = []
if self.responses == None:
self.responses = []
self.invites.sort()
self.responses.sort()
def __str__(self):
return ("""Event('%s', %r, %r)""" % (self.title, self.invites, self.responses))
def __repr__(self):
return str(self)
def __eq__(self, other):
if self.title == other.title and self.invites == other.invites and self.responses == other.responses:
return True
def count_pending(self):
num_pending = 0
lst_noresp = self.invites[:]
for invi in lst_noresp:
if (any(invi.name == resp.name for resp in self.responses)) == True:
lst_noresp.remove(invi)
for invi in lst_noresp:
num_pending += invi.num_invited
return num_pending
e = Event("graduation",[Invitation("A",5),Invitation("B",10),Invitation("C",5),Invitation("D",7)], [Response("A",True,5),Response("B",True,6),Response("C",False,0),Response("D",True,1)])
print(e.count_pending())
My error is that the count_pending() method is only removing some (Invite with name 'A' and Invite with name 'C') objects from the invite list even though all invites have a corresponding response. Why isn't the invi.name == resp.name comparison working properly? Or, is that even the issue?
You have the classic problem of mutating a list while iterating it here:
for invi in lst_noresp: # Iterating
if (any(invi.name == resp.name for resp in self.responses)) == True:
lst_noresp.remove(invi) # Mutating
While there is no documented behavior for it, in practice, this causes the loop to skip the value following each removed element (the iterator stores the current index, and the remove shifts the following elements down, so when you get the next element, you've bypassed the element that was shifted into the space occupied by the removed element), so you're not even checking half your values. Iterate over self.invites, and mutate lst_noresp, and the problem should disappear.
Alternatively, construct a new list and avoid O(n**2) performance:
lst_noresp = [invi for invi in self.invites
if not any(invi.name == resp.name for resp in self.responses)]
which avoids slow removals from the middle of a list in favor of filtering during construction of a new list (O(n) work).

Sometimes None is printed - and sometimes it doesn't, can't get why?

I got this school assignment, here is my code:
class Doubly_linked_node():
def __init__(self, val):
self.value = val
self.next = None
self.prev = None
def __repr__(self):
return str(self.value)
class Deque():
def __init__(self):
self.header = Doubly_linked_node(None)
self.tailer = self.header
self.length = 0
def __repr__(self):
string = str(self.header.value)
index = self.header
while not (index.next is None):
string+=" " + str(index.next.value)
index = index.next
return string
def head_insert(self, item):
new = Doubly_linked_node(item)
new.next=self.header
self.header.prev=new
self.header=new
self.length+=1
if self.tailer.value==None:
self.tailer = self.header
def tail_insert(self, item):
new = Doubly_linked_node(item)
new.prev=self.tailer
self.tailer.next=new
self.tailer=new
self.length+=1
if self.header.value==None:
self.header = self.tailer
it builds a stack, allowing you to add and remove items from the head or tail (I didn't include all the code only the important stuff).
When I initiate an object, if I return self.next it prints None, but if I return self.prev, it prints nothing, just skips, I don't understand why since they are both defined exactly the same as you see, and if I insert only head several times for example for i in range(1,5): D.head_insert(i) and then I print D it prints 5 4 3 2 1 None but if I do tail insert for example for i in range(1,5): D.tail_insert(i) and print D it prints 1 2 3 4 5"as it should without the None. Why is that?
I have included an image:
Keep in mind that you create a Deque which is not empty. You're initializing it with a Node with value None
You're interchanging the value and the Node object. When you're checking if self.tailer.value==None: it's probably not what you're meaning
Following to point 2 is a special handling for the empty Deque, where header and tailer is None
Here is what I have in mind, if I would implement the Deque. I'm slightly changed the return value of __repr__.
class Deque():
def __init__(self):
self.header = None
self.tailer = None
self.length = 0
def __repr__(self):
if self.header is None:
return 'Deque<>'
string = str(self.header.value)
index = self.header.next
while index!=None:
string+=" " + str(index.value)
index = index.next
return 'Deque<'+string+'>'
def head_insert(self, item):
new = Doubly_linked_node(item)
new.next=self.header
if self.length==0:
self.tailer=new
else:
self.header.prev=new
self.header=new
self.length+=1
def tail_insert(self, item):
new = Doubly_linked_node(item)
new.prev=self.tailer
if self.length==0:
self.header=new
else:
self.tailer.next=new
self.tailer=new
self.length+=1
Following Günthers advice, I have modified the __repr__ to this:
def __repr__(self):
string = str(self.header.value)
index = self.header
while not (str(index.next) == "None"):
string += (" " + str(index.next.value))
index = index.next
return string
that did solve the problem, but it is the ugliest solution I have ever seen.
does anyone know a better way?
Following to the question of a better __repr__ method here my proposal. Extend the Deque class with an __iter__ method. So you can iterate over the Deque which is nice to have, e.g.:
for item in D:
print item
Based on that the __repr__ method is easy. Here is the whole change:
def __repr__(self):
return 'Deque<'+' '.join([str(item.value) for item in self])+'>'
def __iter__(self):
index=self.header
while index is not None:
yield index.value
index=index.next

Use a function built outside the class on an object in Python?

I have a class
class List:
def __init__(self,head,tail):
self.head = head
self.tail = tail
def cons(self,item):
return List(item,self)
def isEmpty(self):
return self.head == None
def display(self):
s = "["
first = True
list = self
while not list.isEmpty():
if not first:
s=s+","
first = False
s=s+str(list.head)
list = list.tail
s=s+"]"
return s`
which creates a List object. I have a function (not sure if it works yet);
def sorted(list):
sort = False
i = 0
while i < range(len(list))+1:
if list[i] < list[i+1]:
sort = True
return sort
else:
return sort
and I want to run this function on the List object without adding another method to the class. I know if this was in the class it would just be List.sorted() but how can I run this function on the object without it being the objects method?
sorted(List) doesn't seem to work either. Help please.
Please, please, PLEASE: don't use sorted as a function name. There is a standard (built-in) function sorted() already, that returns a sorted version of an iterable.
Given that your code just checks to see if the List is in order, perhaps you could call it in_order or ordered or is_ascending instead?
That said, let's try to get your code working:
class Node:
def __init__(self, data=None, next=None):
self.data = data
self.next = next
def __str__(self):
return str(self.data)
class List:
def __init__(self, head:Node=None, tail:Node=None):
self.head = tail if head is None else head
self.tail = head if tail is None else tail
def cons(self, node:Node):
node.next = self.head
return List(node, self.tail)
def isEmpty(self):
return self.head == None
def display(self):
return str(self)
def __str__(self):
result = "["
first = True
n = self.head
while n is not None:
if not first:
result += ", "
result += str(n)
if n == self.tail:
break
n = n.next
first = False
result += "]"
return result
def ascendingp(self) -> bool:
n = self.head
last = None
while last != self.tail:
if last is not None and not last.data < n.data:
return False
last = n
n = n.next
return True
tests = (
(1,2,3),
(1,3,2),
(),
)
for t in tests:
last = None
first = None
for i in reversed(t):
n = Node(data=i)
first = n if first is None else first
n.next = last
last = n
l = List(head=last, tail=first)
print(str(l), "Ascending?", l.ascendingp())

Categories