New to OOP and python, I am struggling enormously to grasp what good classes actually are for. I tried to ask help from a lecturer who said "oh, then you should read about general methods to classes". Been putting in a days work but get no where.
I get it that a class allow you to collect an instance structure and methods to it, like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idA.show_list()
But what is even the point of a class if there were not MANY instances you would classify? If I have a method within the class, I must hard code the actual instance to call the class for. What if you want a user to search and select an instance, to then do operations to (e.g. print, compute or whatever)??
I thought of doing it like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idB = Items("idB", "B")
select_item = input("enter item id")
select_item.show_list()
Replacing hard coded variable with input variable doesn't work, probably logically. I then played with the idea of doing it like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
iL = [Items('idA', 'A'), Items('idB', 'B')]
selected_item = input("enter item id")
for selected_item in iL:
print(f'{selected_item.item_id} {selected_item.item_name}')
Now all are called thanks to making it a list instead of separate instances, but how do I actually apply code to filter and only use one instance in the list (dynamically, based on input)?
I would love the one who brought me sense to classes. You guys who work interactively with large data sets must do something what I today believe exist in another dimension.
See examples above^^
It seems you want to find all the instances of a certain element within a class.
This is as simple as:
print([x for x in iL if x.item_id == selected_item])
Now, you may ask why you can't just store the elements of iL as tuples instead of classes. The answer is, you can, but
("idA", "A")
is much less descriptive than:
item_id = "idA"
item_name = "A"
Any code you write with classes, you should in theory be able to write without classes. Classes are for the benefit of the coder, not the end-user of the program. They serve to make the program more readable, which I'm sure you'll find is a desirable property.
Your point here is to lookup for Items instances based on their item_id attribute.
That's a thing to create instances of a class.
It's a completely different thing to search for items objects stored in memory - that is not directly linked to the concept of OOP, classes and instances.
You could use dictionary to store references of your objects and then lookup in your dictionary.
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idB = Items("idB", "B")
lookup_dict = {"idA": idA, "idB": idB}
select_item = input("enter item id")
found_item = lookup_dict.get(select_item)
if found_item:
found_item.show_list()
else:
print(f"item {select_item} not found")
Related
I wrote variables in init's brackets, which should be default if I don’t enter anything, but I get an error if I don’t enter anything and python don’t see the default values for the variables. I am using there input to understand the user what to enter and I would like to leave it.
class News:
def __init__(self, content='Test NEWS name', city='None', news_date_and_time='Not defined'):
self.content = str(input('Write down news content:'))
self.city = str(input('Write down news CITY:'))
self.news_date_and_time = datetime.now().strftime("%d/%m/%Y %H:%M")
pub = News()
I can achieve the input by passing it to the function, as I wrote below, but I want the function use an input and default values which are written
class News:
def __init__(self, content='Test NEWS name', city='None', news_date_and_time='Not defined'):
self.content = content
self.city = city
self.news_date_and_time = datetime.now().strftime("%d/%m/%Y %H:%M")
pub = News('dog ate potatoes','New York')
pub = News(str(input('Input the content:')), str(input('Input the city:')))
How can I implement in the function an input of the variables and the default values are which are written in the function if the wasn't written anything?
but I want the function use an input and default values which are written
This violates the Single Responsibility Principle. Typically we design classes to take arguments similar to how you show in the second code example. This allows the flexibility of reuse because the class doesn't care where the value comes from. In your current situation, you get the values from user input, but you can easily create News objects from database values instead, for example.
To implement default values, I suggest building a separate class or function that decides whether to get input or to use default values. This will make your code in line with the Seperation of Concerns principle.
This function will work the way you want it to work if you test the value of input() and assign the parameter if it's empty:
class News:
def __init__(self, content='Test NEWS name', city='None', news_date_and_time='Not defined'):
self.content = input('Write down news content:') or content
self.city = input('Write down news CITY:') or city
self.news_date_and_time = datetime.now().strftime("%d/%m/%Y %H:%M")
As noted, though, this is bad design, because now it's impossible to create a News object without having it stop your script to ask for input. You should instead do this outside of the constructor:
class News:
def __init__(self, content, city):
self.content = content
self.city = city
self.news_date_and_time = datetime.now().strftime("%d/%m/%Y %H:%M")
pub1 = News("Dog ate potatoes", "New York")
pub2 = News(input("Enter content: "), input("Enter city: "))
Note that there's no reason to have a news_date_and_time parameter since you always use datetime.now in your constructor.
Hi everyone I am currently creating a student class to record down exam results and find the average score. Here's the code I have right now:
class Student:
def __init__(self, name):
self.name = name
self.all_grades = []
def add_exam(self, newest_grade):
self.newest_grade = newest_grade
self.all_grades = self.all_grades.append(newest_grade)
def get_mean(self):
self.average = sum(self.all_grades)/len(self.all_grades)
Josh = Student()
Josh.add_exam(72)
However, when I try to put print(Josh.all_grades), it doesn't return anything and type(Josh.all_grades) returns a none type.
I am really lost and I don't understand why it doesn't return [72]
You've to just type self.all_grades.append(newest_grade) instead of typing self.all_grades=self.all_grades.append(newest_grade). When you type append(), it adds element to list. But it returns None. So when you type <list_variable>.append(<element>) it adds the element, but when you type <list_variable>=<list_variable>.append(<element>) then the value of whole list is now None
Apologies if I explain something wrong or use the wrong wording, my programmer vocabulary isn't the best. If anyone understands my problem and has better ways of explaining it feel free to do so. I have a problem similar to a problem here. I want to remove items from a list that occur in another list. But one list will have strings that reference the variable "name" within class objects.
class sword:
name = 'swordName'
class bow:
name = 'bowName'
class axe:
name = 'axeName'
inventory = [sword, bow, sword, axe]
select = ['bowName', 'swordName']
I want to be able to create a list "selectedItems" with the class objects out of inventory based off of the strings in "select" that are equal to the "name" of the class objects. It also needs to work if "inventory" and "select" both have duplicates in them.
Output:
>> inventory = [bow, axe]
>> selectedItems = [bow, sword]
One other thing I would like the program to ignore if there are more "name"s in select than there are corresponding class objects in "inventory", and to ignore if a string in "select" has no corresponding class objects.
For example, if "inventory" is [sword, axe] and "select" is ['bowName', 'non-existent', 'axeName'], the result is that "inventory" is [sword] and "selectedItems" is [axe].
A simple way of explaining this is that select will take from inventory, but if select can't take from inventory nothing happens.
You may make base class with magic methods __eq__ and __hash__ which can allow you to manage comparing your objects as you want:
class BaseItem:
name = None
def __init__(self):
self.__name = self.name
def __eq__(self, other):
return self.__name == other
def __hash__(self):
return id(self.__name)
def __repr__(self):
return f"'{self.__name}'"
class Sword(BaseItem):
name = "swordName"
class Bow(BaseItem):
name = "bowName"
class Axe(BaseItem):
name = "axeName"
inventory = [Sword(), Bow()]
select = ["swordName", "bowName", "axeName", "swordName", "bowName"]
# casting lists into sets and getting difference between them
result = set(inventory) - set(select)
print(result) # output {'swordName', 'bowName'}
eq - actually is unused here but i added that you can compare your objects with strings, lists etc:
Sword() in ["swordName"] # true
Sword() in ["bowName"] # false
Sword() == "swordName" # true
Sword() == "bowName" # false
hash - need to comparing two objects, actually it use for getting difference between two sets
repr - it is not really required method, it needs just for pretty displaying of objects
selectedItems = list()
# make a new list of the names of the objects in the inventory
# inventory and inventory names have the same index for the same item
inventory_names = [x.name for x in inventory]
for s in select:
if s in inventory_names:
index = inventory_names.index(s)
inventory_names.pop(index)
selectedItems.append(inventory.pop(index))
Hello Stack Overflow!
I am executing a simple command in a program that compiles a report of all the books contained in a library. The library contains a list of shelves, each shelves contains a dictionary of books. However, despite my best efforts, I am always duplicating all my books and placing them on every shelf, instead of the shelf I've instructed the program to place the book on.
I expect I have missed out on some kind of fundamental rule with object creation and organization.
I believe the culprits are the enshelf and unshelf methods in the book class.
Thank you so much for your time,
Jake
Code below:
class book():
shelf_number = None
def __init__(self, title, author):
super(book, self).__init__()
self.title = title
self.author = author
def enshelf(self, shelf_number):
self.shelf_number = shelf_number
SPL.shelves[self.shelf_number].books[hash(self)] = self
def unshelf(self):
del SPL.shelves[self.shelf_number].books[hash(self)]
return self
def get_title(self):
return self.title
def get_author(self):
return self.author
class shelf():
books = {}
def __init__(self):
super(shelf, self).__init__()
def get_books(self):
temp_list = []
for k in self.books.keys():
temp_list.append(self.books[k].get_title())
return temp_list
class library():
shelves = []
def __init__(self, name):
super(library, self).__init__()
self.name = name
def make_shelf(self):
temp = shelf()
self.shelves.append(temp)
def remove_shelf(shelf_number):
del shelves[shelf_number]
def report_all_books(self):
temp_list = []
for x in range(0,len(self.shelves)):
temp_list.append(self.shelves[x].get_books())
print(temp_list)
#---------------------------------------------------------------------------------------
#----------------------SEATTLE PUBLIC LIBARARY -----------------------------------------
#---------------------------------------------------------------------------------------
SPL = library("Seattle Public Library")
for x in range(0,3):
SPL.make_shelf()
b1 = book("matterhorn","karl marlantes")
b2 = book("my life","bill clinton")
b3 = book("decision points","george bush")
b1.enshelf(0)
b2.enshelf(1)
b3.enshelf(2)
print(SPL.report_all_books())
b1.unshelf()
b2.unshelf()
b3.unshelf()
OUTPUT:
[['decision points', 'my life', 'matterhorn'], ['decision points', 'my life', 'matterhorn'], ['decision points', 'my life', 'matterhorn']]
None
[Finished in 0.1s]
..instead of [["decision points"],["my life"],["matterhorn"]]
Use dict.pop() instead of del.
Add self.books = {} to shelf's __init__. Don't declare books outside of the __init__, because if you do so, all of the instances of that class are going to refer to the same thing. Instead, this makes each instance have its own dictionary, which is of course what you want since a book can't be in two shelves at once.
Do the same for library and its shelves and book and its shelf_number.
Pass a library instance as an argument to enshelf and unshelf. When you refer to SPL from within your objects' methods, Python finds that there is no local SPL defined, so it searches for one outside of the local scope; but if you were to try to assign something to SPL or do some other sort of mutative business, you would get an UnboundLocalError.
Bonuses:
class book(object), class shelf(object), and class library(object). (Won't fix your problem, but you should do that anyway.)
You don't need to hash the keys before using them, they will be hashed (if they are hashable, but if you're hashing them, then they are).
There is no need to call super() unless you are inheriting from something, in which case you can delegate a method call to a parent or sibling using it - but you aren't doing that.
get_books() can be implemented as nothing more than return [self.books[k].get_title() for k in self.books.iterkeys()]
Likewise for report_all_books(): return [shlf.get_books() for shlf in self.shelves]. Note that I am not iterating over the indices, but rather over the elements themselves. Try for c in "foobar": print(c) in the interactive shell if you want to see for yourself.
I have defined an object that has several attribute..
class thing(object):
def __init__(self, type, name, attrA, attrB, attrC):
self.type = type
self.name = name
self.attrA = attrA
self.attrB = attrB
self.attrC = attrC
lets say then I have a list of things
self.things=[thing('car','fred',1,2,3),
thing('car','george',a,b,c),
thing('truck','bob',6,7,8),
thing('truck','tom',x,y,z)
]
I then populate a choice box with SOME of the items from that list
for each in self.things:
if each.type == 'car':
self.choiceCar.Append(item=each.name)
When the user selects Bob from the dropdown I have an event for that
def EvtChoice(self,event):
self.Name = event.GetString()
This captures the name of the selection, but how do I get the other attributes? What I am currently doing is
for each in self.items:
if self.Name == each.name
#Get other things here
My thought is that if my list grows large then this loop through my entire list will become very inefficient and really unneeded since the user has already selected the specific item I want. What I think I should be able to do is to get the index of the selected item, but im not sure how to do that, or even if that is the correct way to go about it.
Associating data or objects with wx.Choice or wx.ComboBox is pretty easy. You can see an example using the latter here:
http://www.blog.pythonlibrary.org/2010/12/16/wxpython-storing-object-in-combobox-or-listbox-widgets/
The basic idea is to pass an empty list to the control's constructor and then iterate over the objects and Append them to the control. So something like this:
for obj in self.things:
self.choiceCar.Append(obj.name, obj)
Then in the event handler for the widget, you can get the object back by doing this:
obj = self.choiceCar.GetClientData(self.choiceCar.GetSelection())