Problem When Creating Custom Class in Python - python

With the code below:
class Int_set(list):
def __init__(self):
self.vals=[]
self.index=0
def insert(self, elm): #assume a string of numbers
for x in elm:
if x not in self.vals:
self.vals.append(int(x))
return self.vals
def member(self, elm):
return elm in self.vals
def remove(self, elm):
try:
self.vals.remove(elm)
except:
raise ValueError(str(elm)+' not found')
def get_members(self):
return self.vals[:]
def __str__(self):
if self.vals == []:
return '[]'
self.vals.sort()
result = ''
for e in self.vals:
result = result + str(e) + ','
return f'{{{result[:-1]}}}'
def union(self, other):
'''add all non-duplicate elements from other set to self set'''
print(len(other))
for e in range(len(other)):
if other[e] not in self.vals:
self.vals.append(other[e])
else:
continue
return self.vals
set1=Int_set()
set1.insert('123456')
print(set1.get_members())
set2=Int_set()
set2.insert('987')
print(set2.get_members())
print(set1.union(set2))
I get output:
[1, 2, 3, 4, 5, 6]
[9, 8, 7]
0 # the print(len(other)) in def union
[1, 2, 3, 4, 5, 6]
Notice that the def union(self, other) did not add in all the unique numbers from set2 to set1. However, if I use:
print(set1.union(set2.vals))
then I get:
[1, 2, 3, 4, 5, 6]
[9, 8, 7]
3 # the print(len(other)) in def union
[1, 2, 3, 4, 5, 6, 9, 8, 7]
What is causing this? I assume set2 inside .union(set2) was in the state when it was initialized(thus it's an empty list). But I inserted some numbers inside and printed it out

You call len on the instance of Int_set. This is derived from list and gives you the length of the instance itself which is 0. You need to overwrite __len__ in your class and give back the value of the instances val.
def __len__(self):
return len(self.vals)
And you have to change your union method too. At the moment you iterate over the members of the instance of Int_set and there are none. You have to iterate over the vals attribute.
def union(self, other):
'''add all non-duplicate elements from other set to self set'''
print(len(other))
for element in other.vals:
if element not in self.vals:
self.vals.append(element)
else:
continue
return self.vals
Overall it's unclear why you decided to make Int_set a subclass of list since you use no features from the list class.

Your problem is that you are trying to read values from class itself, you are using other instead of other.vals. You have answered your question when you tried with
print(set1.union(set2.vals))
So you can change your function to this:
def union(self, other):
'''add all non-duplicate elements from other set to self set'''
print(len(other.vals))
for e in range(len(other.vals)):
if other.vals[e] not in self.vals:
self.vals.append(other.vals[e])
else:
continue
return self.vals

Related

Why __str__(self) doesn't work when calling the print() function?

I'm diving into OOP and learning magic (or dunder) techniques. Python 3.8.8.
I created class FreqStack() with a pop() method that removes the most frequent elements and returns an updated stack.
class FreqStack():
def __init__(self, lst:list = None):
if lst is None:
self.stack = []
else:
self.stack = lst[::-1]
def push(self, el: int):
self.stack.insert(0, el)
return self.stack
def pop(self):
if len(self.stack) != 0:
hash_map = {}
for el in self.stack:
hash_map[el] = hash_map.get(el, 0) + 1
most_freq_el = max(hash_map, key=hash_map.get)
while most_freq_el in self.stack:
self.stack.remove(most_freq_el)
return self.stack
else:
return 'Stack is empty!'
def __str__(self):
return '\n|\n'.join(str(el) for el in self.stack)
I also added the dunder method str(), which, as far as I understand correctly, must return a custom string when calling the print() function.
However, the print() function in the example below, instead of returning a string, returns a list.
lst = [1, 1, 1, 5, 5, 5, 3, 3, 3, 7, 7, 9]
freq_stack = FreqStack(lst)
for i in range(6):
print(freq_stack.pop())
Output:
[9, 7, 7, 5, 5, 5, 1, 1, 1]
[9, 7, 7, 1, 1, 1]
[9, 7, 7]
[9]
[]
Stack is empty!
I googled everything related to this problem, and couldn't solve it. What am I doing wrong?
You are printing the return value of pop, not the freq_stack itself.
The __str__ method is for the freq_stack object, so you may try something like:
freq_stack.pop()
print(freq_stack)

Why is this iterator returning none with the expected output?

I am writing an iterator that should return all odd numbers from a giver iter. However, my code is printing none as well as the number I expect returned.
def __iter__(self):
return self
def __next__(self):
k = next(self.other_it)
if (k % 2) != 0: return k
if __name__ == '__main__':
for i in OddIterator([2, 7, 4, 6, 7, 5, 9, 8, 11]):
print(i)

Print stack or row in different ways with same function in Python

My task is to print Stack and Queues in different ways.
I implemented Stack with single linked list and Queue with double linked list.
This is main function where I push elements into Stog(Stack) and Red(Queue).
stavi function is same as enqueue, this is in Queue class.
def stavi(self, data):
if self.head == None:
self.head = Node(data)
else:
newnode = Node(data)
newnode.next = self.head
self.head = newnode
I cannot change main part!
L = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
S = Stog()
R = Red()
for element in L:
R.stavi(element)
This is how I call function ispis(print):
ispis(S)
ispis(R)
And this is how I made function ispis:
def ispis(self):
current = self.head
if (self.head == None):
print ("Lista je prazna")
return
print("<-- ", end= "")
while(current != None):
if (current.next is None):
print(current.data)
else:
print (current.data, end = ", ")
current = current.next
return
Result should be:
ispis(S) <-- 9 6 14 22 5 5
ispis(R) # 10 9 8 7 6 5 4 3 2 1 -->
My results are :
<-- 9, 6, 14, 22, 5, 5
<-- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
I know how to made it for Stack, but how can my code "understand" difference between Stack and Queue. Or should I somehow add elements in Queue from tail ?
Some issues in the information provided:
There is no need to implement a queue with a doubly linked list. A singly linked list is all you need, but with a reference to both its head and its tail.
Your code only populates the queue, not the stack
It makes no sense to expect "9 6 14 22 5 5" as output when the input is "1, 2, 3, 4, 5, 6, 7, 8, 9, 10"
It is not best practice to have a print method. Printing is a concern that should be kept separate from stack/queue implementation logic. Instead define a method that iterates the values in the list -- for instance by implementing __iter__, and if you need a formatted string, __str__ or __repr__.
If a function is to behave differently for instances of different classes, then it is better practice to define it on each class separately.
Here is an idea of an implementation, where Queue inherits from Stack. As I don't understand the logic for printing the "#" symbol, I will use "-->" instead, such that an arrow towards the list indicates the side where future values will be added, and an arrow away from the list indicates the side where values will be removed from the list.
class Node:
def __init__(self, value, nxt=None):
self.value = value
self.next = nxt
class Stack:
def __init__(self, values=None):
self.head = None
if values:
self.insert(*values)
def insert(self, *values):
for value in values:
self.head = Node(value, self.head)
def extract(self):
if self.head:
value = self.head.value
self.head = self.head.next
return value
def __iter__(self):
node = self.head
while node:
yield node.value
node = node.next
def join(self):
return " ".join(map(str, self)) or "(empty)"
def __str__(self):
return "<--> " + self.join()
class Queue(Stack):
def __init__(self, values=None):
self.tail = None
super().__init__(values)
def insert(self, *values):
if values and not self.tail:
self.head = self.tail = Node(values[0])
values = values[1:]
for value in values:
self.tail.next = self.tail = Node(value)
def __str__(self):
return "<-- " + self.join() + " <--"
values = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
s = Stack(values)
q = Queue(values)
print("Stack", s)
print("Queue", q)
As Queue inherits from Stack the methods to add/remove values are called insert and extract for both classes. Queue overrides the Stack implementation of insert to change the FILO into FIFO logic.

Replace the list element of class

This is a homework to select a card from cards. After a card being selected, the card in list should be replaced by None.
class Card():
def __init__(self,cards):
self._cards = cards
def get_cards(self):
return self._cards
def get_card(self,slot):
return self._cards[slot]
It is not allowed to change or add any other code for above class.
And the expected output shows below
card1 = Card([1,2,3,4])
# card1.get_cards()=None (I try this but it does not work because it is not allowed to assign item to method)
print(card1.get_card(0)) # expectation: 1
print(card1.get_cards()) # expectation: [None, 2, 3, 4]
print(card1.get_card(1)) # expectation: 2
print(card1.get_cards()) # expectation: [None, None, 3, 4]
When you pass a list in python, behind the scenes it is just a ref to this list.
So we can use this to our advantage here.
# You almost has done it right
# take the list ref
cards = card1.get_cards()
# and now mutate the list inside
cards[slot] = None
The other solution is to monkey patch the get_card method, so the definition class itself won't be affected in the runtime we will attach new implementation to get_card method. If You need this second solution let me know.
class Card():
def __init__(self,cards):
self._cards = cards
def get_cards(self):
return self._cards
def get_card(self,slot):
return self._cards[slot]
Monkey patching the get_card function
def get_my_card(self, slot):
card, self._cards[slot] = self._cards[slot], None
return card
Time to test the above code
card1 = Card([1,2,3,4])
Card.get_card = get_my_card
print(card1.get_card(0)) # expectation: 1
print(card1.get_cards()) # expectation: [None, 2, 3, 4]
print(card1.get_card(1)) # expectation: 2
print(card1.get_cards()) # expectation: [None, None, 3, 4]
Output
1
[None, 2, 3, 4]
2
[None, None, 3, 4]
Solution 2: Object-Oriented Approach
class Card(): def __init__(self,cards):
self._cards = cards
def get_cards(self):
return self._cards
def get_card(self,slot):
return self._cards[slot]
Create a derived class and overrride get_card method
class MyCard(Card):
def get_card(self, slot):
card, self._cards[slot] = self._cards[slot], None
return card
Test the code
card1 = MyCard([1,2,3,4])
print(card1.get_card(0)) # expectation: 1
print(card1.get_cards()) # expectation: [None, 2, 3, 4]
print(card1.get_card(1)) # expectation: 2
print(card1.get_cards()) # expectation: [None, None, 3, 4]

Python decorating property setter with list [duplicate]

This question already has answers here:
python: how to have a property and with a setter function that detects all changes that happen to the value
(3 answers)
Closed 6 years ago.
globalList = []
class MyList:
def __init__(self):
self._myList = [1, 2, 3]
#property
def myList(self):
return self._myList + globalList
#myList.setter
def myList(self, val):
self._myList = val
mL1 = MyList()
print("myList: ", mL1.myList)
mL1.myList.append(4)
print("after appending a 4, myList: ", mL1.myList)
mL1.myList.extend([5,6,"eight","IX"])
print("after extend, myList: ", mL1.myList)
Result:
myList: [1, 2, 3]
after appending a 4, myList: [1, 2, 3]
after extend, myList: [1, 2, 3]
The problem I am facing is that mL1.myList.append(4) and mL1.myList.extend([5,6,"eight","IX"]) do not modify the _myList attribute in the mL1 object. How could I do to resolve the problem?
I define a method append() and a method extend() for the class object. It respectively appends to member myList and extends member myList.
global globalList
globalList = []
class MyList():
def __init__(self):
self._myList = [1, 2, 3]
#property
def myList(self):
return self._myList + globalList
#myList.setter
def myList(self, val):
self._myList = val
def append(self, val):
self.myList = self.myList + [val]
return self.myList
def extend(self, val):
return self.myList.extend(val)
mL1 = MyList()
print("myList: ", mL1.myList)
mL1.append(4)
print("after appending a 4, myList: ", mL1.myList)
mL1.myList.extend([5,6,"eight","IX"])
print("after extend, myList: ", mL1.myList)
result is
>>>
('myList: ', [1, 2, 3])
('after appending a 4, myList: ', [1, 2, 3, 4])
('after extend, myList: ', [1, 2, 3, 4, 5, 6, 'eight', 'IX'])
I would subclass list and override a few methods:
import itertools
class ExtendedList(list):
def __init__(self, other=None):
self.other = other or []
def __len__(self):
return list.__len__(self) + len(self.other)
def __iter__(self):
return itertools.chain(list.__iter__(self), iter(self.other))
def __getitem__(self, index):
l = list.__len__(self)
if index > l:
return self.other[index - l]
else:
return list.__getitem__(self, index)
It should work with just about everything:
In [9]: x = ExtendedList([1, 2, 3])
In [10]: x
Out[10]: [1, 2, 3]
In [11]: x.append(9)
In [12]: x
Out[12]: [9, 1, 2, 3]
In [13]: x.extend([19, 20])
In [14]: x
Out[14]: [9, 19, 20, 1, 2, 3]
In [15]: sum(x)
Out[15]: 54

Categories