List class retaining old values for new list Python - python

I have created a list class with a few simple methods.
class ListQ(object):
"""List object creation"""
def __init__(self, earlier_list=[]):
self.list=earlier_list
def isEmpty(self):
"""checks if list is empty"""
if len(self.list)==0:
return(True)
else:
return(False)
def put(self, new_item):
"""puts new_item last in list"""
self.list.append(new_item)
def get(self):
"""returns the next item in line"""
self.first_item = self.list.pop(0)
return self.first_item
I also have a function to help create these lists. (Helping to solve a magic card trick btw)
def create_card_list(card_list=None):
"""creates listQ of cards from user input"""
if card_list!=None: #if user already has list of cards
for item in card_list:
item=replace_card_strings(item) #replaces any strings in list
if item == False:
print('One of your cards does not exist, list creation failed')
return(False)
cards=ListQ(card_list)
The replace_card_strings() function has been properly tested and I do not believe it's at fault but here it is anyway.
def replace_card_strings(word):
"""replaces strings of words with their numbers and returns edited list"""
if type(word)!=int:
if word.lower()=='one':
word=1
elif word.lower()=='two':
word=2
elif word.lower()=='three':
word=3
elif word.lower()=='four':
word=4
elif word.lower()=='five':
word=5
elif word.lower()=='six':
word=6
elif word.lower()=='seven':
word=7
elif word.lower()=='eight':
word=8
elif word.lower()=='nine':
word=9
elif word.lower()=='ten':
word=10
elif word.lower()=='jack':
word=11
elif word.lower()=='queen':
word=12
elif word.lower()=='king':
word=13
elif word.lower()=='ace':
word=1
else:
word=False #if card doesnt exist
return(word)
When I run the following test is when the problem start.
cards=create_card_list(['one', 2, 3])
one=cards.get()
two=cards.get()
three=cards.get()
print(one, two, three)
if one==1 and two==2 and three==3:
print("create_card_list gives the correct answer")
else:
print("create_card_list gives the wrong answer")
It prints that the function has given the wrong answer and
the print(one, two, three) prints one 2 3 which is the original list.
Anybody have an idea where I've messed up?

You're not replacing items in the list, only in the loop's context:
Replace the entire loop:
for item in card_list:
item=replace_card_strings(item) #replaces any strings in list
....
Which does not do anything to the list (it does not "replace" the item).
With python's map which is used in order to apply a function to an iterable and create a list of results:
card_list = map(replace_card_strings, card_list)
If you don't want to use map you can use list comprehension:
card_list = [replace_card_strings(card_str) for card_str in card_list]
Now you'll have:
def create_card_list(card_list=None):
"""creates listQ of cards from user input"""
if card_list != None:
card_list = map(replace_card_strings, card_list)
cards=ListQ(card_list)
Tip:
You can replace the lengthy function replace_card_strings:
def replace_card_strings(word):
"""replaces strings of words with their numbers and returns edited list"""
word = word.lower()
string_to_int = {"one":1, "two":2 ...}
return word in string_to_int and string_to_int[word]
This works since and returns the last Truth-y result or the first False-y result.
You can also use dict.get:
# return word in string_to_int and string_to_int[word]
# try and read value from dict, return False otherwise
return string_to_int.get(word, False)
Which is cleaner (thanks #padraic).
Tip 2: You don't want two places referencing, and possibly mutating, the same list. It seems like you're keeping a reference to the same list that's used to construct a card list outside the generated object. This is prone to errors. What you should do is instead copy the list when it's passed:
def __init__(self, earlier_list=[]):
self.list=earlier_list[:]
Tip 3: python has a known "gotcha" with mutable default argument, which is exactly what you use here (earlier_list=[]). This is prone to errors as well since python binds default arguments once. You should instead have:
def __init__(self, earlier_list=None):
# copy passed list or create a new one if none passed.
self.list=earlier_list[:] if earlier_list else []

Related

Why won't "if this in thislist" return True when user inputs "this" and "thislist"?

I'm trying to understand how some code works so I've made my own simple code that should act like how the more complex code works. However, it seems that I'm missing something. I made a simple program to pass a "trigger" to compare to my list.
def function(findme, thislist):
print("FindMe: {}".format(findme))
print("In This List: {}".format(thislist))
if findme in thislist:
print("In!")
return thislist
while(True):
find = []
list = []
find.append(input("Input number to find: "))
print("Find: {}".format(find))
list.append(input("Input 1st number: "))
list.append(input("Input 2nd number: "))
print("List: {}".format(list))
result = function(find, list)
print(result)
if result is not None:
print("returned list")
print(list)
else:
print("nothing returned")
The code finishes returning None and printing "nothing returned". I input 1 as find and 1, 2 as list. What am I missing here?
p.s. I'm new to python and stack overflow, forgive my messy post :x
You cannot search if a list contains a sublist that way.
If you want to check if multiple elements are in the list, then you can do this instead
if (all(x in thislist for x in findme)):
print ("Sublist in the mainlist")
return thislist
This is one way to do it, if you do not have to compare lists, then you could simply use the code which you have just replace findme with findme[0]
The problem is, that the search value is a list itself. So you check if a list with one value is in the list with the values you want to find. It is not. The one value in the search list might be in the list with the values.
So you should use a single value as your search term.
I optimized the names of some variables, used f-strings for formatting and used fixed values for filling the list so you can test it faster.
def is_in_list(findme, data):
print(f"FindMe: {findme}")
print(f"In This List: {data}")
if findme in data:
print("In!")
return True
return False
find = input("Input number to find: ")
print(f"Find: {find}")
data = []
data.append("42")
data.append("23")
print(f"List: {data}")
if is_in_list(find, data):
print(data)
else:
print("nothing returned")
One more change. It doesn't make much sense to return the same list from the function that you put in there as a parameter. So I used return values of True and False instead.

How to recursively append to an empty list?

I'm new to recursion and finding it pretty difficult to grasp. I can't figure out how to append an empty array if I can't directly "touch" it. If its a string I would add the value each time. If it was a number that involved multiplication, I could multiply it each time, but with an array, I don't know what to do.
I dont know how to append to an empty array without being able to directly "touch" it.
This is what I've done so far:
def laugh(num):
if num == 0:
return []
# This doesnt work since we can't append a function call. I'm unsure what to do.
return laugh(num - 1).append("ha ")
print(laugh(3)) -> ["ha, ha, ha"]
If could do this easily if i could just return a string of "Ha"'s instead. I could return an empty string and just add a "Ha" for each step.
You can modify it like:
def laugh(num):
if num == 0:
return []
haha = laugh(num-1)
haha.append("ha")
return haha
Since append does not return the modified list, you have to do it in two steps. Using concatenation and the ternary operator, you can shrink this to:
def laugh(num):
return laugh(num-1) + ["ha"] if num else []
In this case you are mutating the list by calling append on it. What you want to do is return a new list:
def laugh(num):
# base case
if num == 0:
return []
# recursive case
return ["ha"] + laugh(num-1)

How to search list of objects for index of minimum

I want to search through a list of objects for the lowest number present in an attribute of the objects.
This is easy enough using a list comprehension and the min function. The issue comes when I want to find the index of the object.
class School:
def __init__(self, name, num_pupils, num_classrooms):
self.name = name
self.num_pupils = num_pupils
self.num_classrooms = num_classrooms
def students_per_class(self):
return self.num_pupils / self.num_classrooms
def show_info(self):
print(f"{self.name} has {self.students_per_class():.2f} students per class.")
def string_checker(question):
valid = False
while not valid:
try:
response = str(input(question))
if all(character.isalpha() or character.isspace() for character in response):
valid = True
return response
else:
print("Enter a string containing no numbers of special characters. ")
except ValueError:
print("Enter a string containing no numbers of special characters. ")
def num_checker(question):
valid = False
while not valid:
try:
response = int(input(question))
if (response):
valid = True
return response
else:
print("Enter an integer containing no letters or special characters. ")
except ValueError:
print("Enter an integer containing no letters or special characters. ")
def new_school():
school_name = string_checker("School Name: ")
num_pupils = num_checker("Number of Pupils: ")
num_classrooms = num_checker("Number of Classrooms: ")
return School(school_name, num_pupils, num_classrooms)
if __name__ == "__main__":
schools = []
school = School("Darfield High School", 900, 37)
schools.append(school)
school.show_info()
for i in range(1):
schools.append(new_school())
for school in schools:
school.show_info()
print(min(school.students_per_class() for school in schools))
# This works fine and prints the value of the lowest number
print(schools.index(min(school.students_per_class() for school in schools)))
# This doesn't work as it tries to find the index of the value returned
# from the min function as it should.
You can use min's key argument to search by index:
index = min(range(len(schools)), key=lambda i: schools[i].students_per_class())
print(schools[index])
A key provides the values that will be compared instead of the actual sequence. Here, my sequence is range(len(schools)), which is just the indices of all the elements. But instead of finding the minimum index, I am making it so that we find the minimum of schools[i].students_per_class() for each index i.
If you wanted to be able to compare, sort, find the min/ max on the items directly you could use the "dunder" methods. These methods allow you to overload built in functions on classes.
For instance if you had a class like this
class Item:
def __init__(self, value):
self.value = value
def __lt__(self, other):
return self.value < other.value
def __eq__(self, other):
return self.value == other.value
You can then create two instances and compare them directly like this,
A = Item(1)
B = Item(2)
print(A < B) # Prints True
or if you had a list of items
items = [A, B]
You can then get the minimum item by going
min_item = min(items)
or the index of it by going
min_item_index = items.index(min(items))
although it may be enough to just have a reference to the minimum item.
Use enumerate to iterate through lists while keeping track of indices:
min(((i, school.students_per_class()) for i, school in enumerate(schools)), key=lambda x: x[1])
(i, school.students_per_class()) is a tuple and using key parameter of min(), we ask Python to find minimum of school.students_per_class(), which subsequently returns it's index along with it.
Read about lambda here.
I think you may be looking for np.argmin function, if you provide a list as an input it will return the index of the minimal element.
in this case it would be:
import numpy as np
min_ind = np.argmin([school.students_per_class() for school in schools])
print(schools[min_ind])
#Tomas Ordonez
Your answer is here:
def minInt(instanceList):
sorted_instanceList = sorted(instanceList, key=lambda instance: instance.int)
minIndex = instanceList.index(sorted_instanceList[0])
return minIndex

stack function python error

I created the following stack class for a project and am having trouble getting it to function properly. I can't tell if i made the error or it is an error in the main function i was given by my TA, anyways here is my code:
class Stack:
#takes in the object as input argument
#will not return anything
def __init__(self):
#initialise an instance variable to an empty list.
self.items=[]
#takes in the object as input argument
#return value Type: True or False
def isEmpty(self):
#check if the list is empty or not. If empty, return True else return False
if self.items == []:
return True
else:
return False
#takes in the object as the first argument
#takes the element to be inserted into the list as the second argument
#should not return anything
def push(self, x):
#add the element to be inserted at the end of the list
self.items.append(x)
#takes in the object as the input argument
#if the list is not empty then returns the last element deleted from the list. If the list is empty, don't return anything
def pop(self):
#check if the list is Empty
#if Empty: print the list is empty
#if the list is not empty, then remove the last element from the list and return it
if self.isEmpty()==True:
print("the list is empty")
else:
return self.items.pop()
#takes in the object as the input argument
#should not return anything
def printContents(self):
#if the list is not empty, then print each element of the list
print("The content of the list is", self.items)
Based on the comments can anyone give me any advice on how I might make this work more appropriately? Sorry I am not a computer scientist and i am trying my hardest to understand classes and functions for my python class.
from stack import *
def main():
s = Stack()
s.push(1)
s.pop()
s.pop()
s.push(2)
s.push(3)
s.push(4)
s.printContents()
main()
You should take a good look in spaces and alignment. For example printContents is not properly aligned. Note the proper alignment is very, very important in python.
Also you are not printing in printContents. This should work:
class Stack:
#takes in the object as input argument
#will not return anything
def __init__(self):
#initialise an instance variable to an empty list.
self.items=[]
#takes in the object as input argument
#return value Type: True or False
def isEmpty(self):
#check if the list is empty or not. If empty, return True else return False
if self.items == []:
return True
else:
return False
#takes in the object as the first argument
#takes the element to be inserted into the list as the second argument
#should not return anything
def push(self, x):
#add the element to be inserted at the end of the list
self.items.append(x)
#takes in the object as the input argument
#if the list is not empty then returns the last element deleted from the list. If the list is empty, don't return anything
def pop(self):
#check if the list is Empty
#if Empty: print the list is empty
#if the list is not empty, then remove the last element from the list and return it
if self.isEmpty():
print("the list is empty")
else:
return self.items.pop()
#takes in the object as the input argument
#should not return anything
def printContents(self):
#if the list is not empty, then print each element of the list
print("the contents of the list are", self.items)
def main():
s = Stack()
s.push(1)
s.pop()
s.pop()
s.push(2)
s.push(3)
s.push(4)
s.printContents()
main()
You can see it working online here:
https://repl.it/BZW4
Everybody has to start sometime so no worries, and I hope you are not offended by my extended critique, as I am just trying to be helpful.
You probably want the top declaration to be:
class Stack(object):
Leaving off the (object): part is a deprecated form that makes certain features of the class different in ways you probably don't want.
Secondly, declaring an isEmpty method is not really a normal "pythonic" approach. The strong expected python convention is to simply check the truth value of your object, with empty being False. Typical python users of your class will not expect an isEmpty method. You can control the behavior of your object's truth value by defining the special nonzero which you could write like this:
def __nonzero__(self):
return bool(self.items)
This way somebody could use your class like:
stack = Stack()
stack.append("apple")
stack.append("pear")
if stack:
print "stack has stuff in it"
else:
print "stack is empty"
Moreover, there are other things you should overload to make a good stack class. For example you ought support len(stack) and you ought to be able to iterate over your stack. You can define special len and iter methods to do this. Notice if you define the len then you don't need to define the iszero (python will consider your stack False if len returns 0 and you have not defined iszero).
Your printContents method will not print or write anything without a print or write statement, but will return the last element in the stack and throw an index error if the stack is empty. To iterate over each item and print it you could write it like this (from the top item of stack to the first one):
def printContents(self):
for item in reversed(self.items):
print item
However it would be more pythonic to define an iteration method and the use that, so your user can iterate over your stack and print it themselves:
def __iter__(self):
for item in self.items:
yield item
# later in code .....
stack = Stack()
stack.append("apple")
stack.append("pear")
for item in stack:
print item
Hopefully these tips may prove useful. Keep at it, and you will get it before long.
Modify your pop definition in class to this
def pop(self):
#check if items is empty
if len(self.items)==0:
#if empty
return "Nothing to pop"
else:
#if not empty
return str(self.items.pop())+" Popped"

Filter( ) function takes function with two inputs

I'm trying to write a filter function that pass me a list of dictionary words that can be formed by letters from a rack.
def test(Rack, word):
"""test whether a string can be formed with the letters in the Rack."""
if word == "" or Rack == []:
return True
elif word[0] in Rack:
return True and test(Rack, word[1:])
else:
return False
Then my map function will need the test function.
def stringsInDic(Rack, dictionary):
return filter(test(Rack, dictionary) == True, dictionary)
But as we can see, the second input of filter should be an element from the dictionary, which is what filter puts in. So I'm not sure how to write the second argument in test.
Please help!!! Thanks!
You can use functools.partial:
def stringsInDic(Rack, dictionary):
func = functools.partial(test, Rack)
return filter(func, dictionary)
partial allows you to create a sort of place-holder function, which you can add more arguments to later. So func becomes test(Rack, ...). If you were to later call func(something), You'd really be executing test(Rack, something).

Categories

Resources