Queston about define a function in a class - python

if len(self.students) < self.max_students
Not sure what the above codes are used for on the following statement, if anyone can help?
class course:
def __init__(self,name,max_students):
self.name = name
self.max_students = max_students
self.students = [] #empty list
def add_student(self,student):
if len(self.students) < self.max_students:
self.students.append(student)
return True
return False

This statement check before adding a student to the list that the number of the students in the course is smaller than the maximum students can join the course( max_students ). if the number of the students is smaller than the maximum the code add the variable student to the list.
self is used to access in the class atributes(variables, functions, etc...)
len is used to check the length of an object
The class defined in the code have three variables:
max_students - a number that save the maximum number of students that can join the course
students - list that saves the students that are in the course
name - a string that contain the course name

if len(self.students) < self.max_students
Here self.max_students defines the capacity of the student's list or it's just like a maximum number of students should be allowed inside a class.
So, here we are checking whether the number of students in a class should not exceed the capacity.

It was to control that the max_student value for the instance of this class is not exceeded. That permits to avoid to add indefinitely values

Related

Python, most compact&efficeint way of checking if an item is any of the lists (which are inside dictionaries)?

I have a dictionary with lists (with strings inside) and I need I need to check if a string appears anywhere among those lists.
Here is an example
classes = {
"class_A" : ["Mike","Alice","Peter"],
"class_B" : ["Sam","Robert","Anna"],
"class_C" : ["Tom","Nick","Jack"]
}
students=["Alice","Frodo","Jack"]
for student in students:
if student *in any of the classes*:
print("Is here")
else:
print("Is not here")
For every student in the list I provided: if that student is in any of the classes, do A, if not do B.
Currently the output is Is here, Is not here, Is here
Here is my current code:
studentsInClasses=[]
for studentsInClass in classes.values()
studentsInClasses+=studentsInClass
students=["Alice","Frodo","Jack"]
for student in students:
if student in studentsInClasses:
print("Is here")
else:
print("Is not here")
But this is happening inside a complex structure of classes, functions and loops so it become a major performance issue as soon as I scale up the inputs.
Here is something that I do like, but is a bit annoying as I have to make sure that whatever function my code is in, has access to this one:
def check(student,classes):
for value in classes.values():
if student in value:
return True
return False
It is probably as good as it gets, but I would like to know if there is a simple one liner that does the job.
Requirements:
Does not create a copy of the lists
Does not rely on keys in any way
Preferably nice and simple
Isn't an over-engineered superefficient solution
I am new to stack overflow, if I am doing any major mistakes in my way of posting please do tell, I will try to improve my question writing.
Thanks
If a generator expression is acceptable with regards to your requirements, then:
def check(student, classes):
return any(student in value for value in classes.values())
And to get a boolean for each student, you could create this function:
def checkall(students, classes):
return [any(student in value for value in classes.values())
for student in students]
For your sample data, this would return [True, False, True].
If allowed, I guess you'd increase performance if you delete the entry within the class, whenever you had a match.
Use sorting beforehand
studentsInClasses=[]
for studentsInClass in classes.values()
studentsInClasses+=studentsInClass
studentsInClasses = sorted(studentsInClasses)
students=sorted(["Alice","Frodo","Jack"])
lastMatch=0
for i in range(len(students)):
student = students[i]
try:
lastMatch = studentsInClasses[lastMatch:].index(student)
print(f"{student} in class")
except ValueError as e:
pass
So if you want to just print is here or is not here here is an example:
classes = {
"class_A" : ["Mike","Alice","Peter"],
"class_B" : ["Sam","Robert","Anna"],
"class_C" : ["Tom","Nick","Jack"]
}
for line in str(classes).split(","):
if student in line:
print("Student in here")
else:
print("Student not here")
Since this is in a loop, you should create a set for all of the values:
from itertools import chain
values = set(chain(*classes.values()))
students=["Alice","Frodo","Jack"]
for student in students:
if student in values:
print("Is here")
else:
print("Is not here")
The reason is that a set lookup is a constant time lookup, and in a tight loop makes a big difference
How about making the list of students in each class a set?
Then the lookup time will be o(1) and you can loop over n classes.
The you can have:
class_to_students = {
"class_A" : {"Mike","Alice","Peter"},
"class_B" : {"Sam","Robert","Anna"},
"class_C" : {"Tom","Nick","Jack"}
}
students=["Alice","Frodo","Jack"]
for student in students:
for class_students in class_to_students.values():
if student in class_students:
print(f"{student} Is here")
break
else:
# loop was not broken out of
print(f"{student} Is not here")
-->
Alice Is here
Frodo Is not here
Jack Is here
If you exclude a solution like this, then you are stuck with your n*m solution where n is the number of classes and m is the number of students in each class.
Is this something you are looking for?
classes = {
"class_A": ["Mike","Alice","Peter"],
"class_B": ["Sam","Robert","Anna"],
"class_C": ["Tom","Nick","Jack"]
}
students = ["Alice", "Frodo", "Jack"]
res = [student in class_ for class_ in classes.values() for student in students ]
print(res)

How to compare user input to class object data?

total beginner here.
I'm trying to understand how to compare user input data to preset class data. Let's say class = Person, with the age, weight, and name.
And I've got two people, johnsmith = Person(50, 200, "John Smith")
tomjones = Person(40, 220, "Tom Jones")
I want to prompt the user to input the name Tom, and have check all "name" attributes for the Person class. So something like,
person = input(print("Insert the person's name: ")) entering "Tom" and then returning a list of all Toms for the user to select, or if there's only one, confirm that the user in fact did mean Tom Jones.
Should I create an array like people = [tomjones, johnsmith, (etc)], and somehow search inside that?
Please point me in the right direction on this, thanks.
Should I create an array-like people = [tomjones, johnsmith, (etc)], and somehow search inside that?
Yes.
Although ihough if you expect a very large number of "people" and frequent queries always on the same field (e.g. you're always searching for a specific person by its name) you may also want to create an index to speed up searches e.g. create a dict (possibly a WeakValueDictionary) which maps whatever your search key is to the proper person.
This is because iterating a list is cheap and efficient but going through the entire list is linear time (O(n)) so if your list is very long and you go through it a lot, it adds up. Building an index is expensive (and requires going through the entire list at least once) and there is more setup to the lookup but setup aside the lookup is constant time (O(1)).
Storing the instances of your Person class in a list would be a way of doing this. You can then loop through the list to match the names.
e.g.
class Person():
def __init__(self, age, weight, name):
self.age = age
self.weight = weight
self.name = name
all_persons = []
all_persons.append(Person(50, 200, "John Smith")) # add your Person instance to a list
all_persons.append(Person(40, 220, "Tom Jones"))
name_input = input("Insert the person's name: ")
for person in all_persons: # loop through list of all Person instances
if name_input.lower() in person.name.lower(): # lower() to handle case
# do something with person

Changing attributes based on user input

Okay, I have a class which has 10 objects, these have the attributes self.planet, self.distance, self.distsquared, self.radius, self.diamater where distance/distsquared/radius/diamater are all integers. And I would like to make a function where the user searches for a planet name, and then changes one of the attributes.
For example, the user should input the name "Jupiter", which would then find the object, and the next row of the function would ask the user to add a certain sum to the attribute self.distance.
Currently the first class is set up as following:
class Planets():
def __init__(self, planetName, dist, radius, diameter):
self.planetName= planetName
self.dist= dist
self.radius= radius
self.diameter= diameter
This is then retrieved through a planetObjects=[Planets(*p) for p in planetList] This is the object-list I would like to turn into a dictionary so the user can search for planetName, and alter the distance
Some users suggested I use a dictionary for this, but I have no idea how to go about doing that. Currently my class turns a list of lists into a list of objects, these objects have these attributes that the user is supposed to be able to change by searching for the Planet name, and then changing one of its attributes.
The class is currently just a simple class which has a constructor and a __str__ function
Meaning, function starts, asks the user something like "Which planet would you like to alter?", the user inputs "Jupiter" and the program asks, "How has the distance to Jupiter changed?" where the user adds for example 450 or so.
The current code I have is a function which opens an infile and turns it into a list of lists. This list is then turned into objects. I turned it into objects to easily be able to sort it and add new values based on previous values. But at this point the user also has to be able to alter values by searching for a planet name and then changing one of the attributes - this is where I am lost and need help!
Is there any way to do this? Thanks in advance!
In psuedocode:
class Planet(object):
# Define your planet class here
# any attributes that you do NOT need the user to be able to edit should start with _
Planets = [Planet('Mercury'.....
#or better
PlanetDict = {'Mercury':Planet(....
which = PromptUserForPlanet()
p = PlanetDict.get(which) # or find and return it if you didn't use a dictionary
for att in dir(p):
if not att.startswith('_'):
input = raw_input('%s: (%s)' % (attr, repr(getattr(p,attr)))
if len(input) > 0:
setattr(p,att,input) # You may wish to do some type conversion first
Since p is a reference to the dictionary entry you will change the main object.
Given your class Planets, this may be solved like this. I'm assuming that planetList is structured like in this code. If it is not, you may have to alter the code a bit.
def increment_dist(planets):
name = raw_input('Please enter planet name')
try:
planets[name].dist += int(raw_input('Increment distance by (integer)'))
except KeyError:
print('No planet called {}'.format(name))
except ValueError:
print('That is not an integer')
planetList = [('Tellus', 1, 2, 4), ('Mars', 1, 3, 9)]
planet_dict = {name: Planets(name, dist, radius, diameter) for
name, dist, radius, diameter in planetList}
increment_dist(planet_dict)

Python 3 Classes and Dictionary

I'm working on a project here and am pretty confused as of how to handle this next part. Included is the project description.
Student class: The student class will store the information about the student id, student’s first and last names, and a dictionary named grades that contains the course number and letter grades of the classes the student has taken. You will write accessor methods for student id, first name, and last name and mutator methods for student’s first name and last name. There will be two more methods in the Student class as shown below.
getCourseNumbers(): it returns a list of course numbers that the student has taken. It returns an empty list if the student has not taken any courses.
getGrade(course_no): it returns the grade the student has received in the course identified by the parameter course_no. If the given course number is not found, this function returns ‘Z’ to indicate that.
addCourse(course_no, grade): adds a course with the course number and grade
updateCourse(course_no, grade): updates an existing course’s grade
deleteCourse(course_no): deletes the course from the dictionary.
Here is my code thus far:
class Course:
def __init__ (self, __crn, __ch, __ins):
self.__crn = crn
self.__ch = ch
self.__ins = ins
def coursename(self):
return self.__crn
def credithour(self):
return self.__ch
def instructor(self):
return self.__ins
class Student:
def __init__(self, id, sfirst, slast):
self.sid = sid
self.sfirst = sfirst
self.slast = slast
def studentid(self):
return self.sid
def studentfirst(self):
return self.sfirst
def studentlast(self):
return self.slast
def main():
course = Course('CSC 1100', '4', 'Name')
print (course.coursename())
print (course.credithour())
print (course.instructor())
main()
So, my question really is. Now that I've made my initial two classes, I'd like to create a dictionary that contains the coursename and the students grade. What's the best way to do this? I'm kind of at a loss and have tried many different ways with no success.
Why do you have a Course class? Nothing in the question asks for one; in fact, it specifies that you are to store the information as a dictionary.
I don't agree with the request for accessor methods; that is a Java idiom, not Pythonic. Similarly, the given method names are javaCase, contrary to PEP8.
The code then reduces to
class Student(object):
def __init__(self, id, firstname, lastname, grades=None):
self.id = id
self.firstname = firstname
self.lastname = lastname
self.grades = {} if grades is None else dict(grades)
def get_course_numbers(self):
return self.grades.keys()
def get_grade(self, course):
return self.grades.get(course, 'Z')
def add_course(self, course, grade):
self.grades[course] = grade
... I'll leave the last couple of methods as an exercise ;)
Recall that a dictionary is just a key-value mapping - it associates keys with particular values. So what you're being asked for is just storing with each student a mapping from a unique identifier for a course to a letter grade (stored e.g. as a string). A dictionary is an excellent tool for this task.
Recall that you can use class instances as keys, even if instances of that class are mutable. The reason is that it's not really the instance itself being used as a key, but its unique identifier (memory address).
It would probably be more apt, though, to have a way to map course numbers to Course instances. Notice the keyword map there? You could also store all the courses in a list and do a search every time you wanted to find a course by number, but that would be slow (for len(courses) -> Infinity).

(Python) Creating a list with a unique automatically generated name [duplicate]

This question already has answers here:
How can you dynamically create variables? [duplicate]
(8 answers)
Closed 8 years ago.
I am trying to automate populating a town by randomly generating households. I generate the name of the town, generate the number of households, the last name of each household and number of occupants in each. That much is fine. I am now, however, trying to create each individual, to generate a first name, a sex, an age and an occupation, and I'd like to store this data in a list as well, one list containing the attributes of each person. The problem I'm running into is that I want to use a for loop, something like:
#houseArray[currentFam][1] is the number of members in the current house.
for currentFam in range(houseArray[currentFam][1]):
uniquelyNamedArray[0] = genSex()
uniquelyNamedArray[1] = genFirstName()
uniquelyNamedArray[2] = genAge()
So... look at the data of the first household, use a for loop to iterate through each member assigning stats, then go to the next household and do the same, progressing through each household. My problem lies in not knowing how to assign a unique name to each array created by the for loop. It doesn't really matter what the name is, it could be anything as long as each person has their own uniquely named array storing their attributes.
Use a dictionary with the person's name as the key. Like:
people = {}
people["Billy Bloggs"] = ['23','Male','263 Evergreen Tce'] # store to dict
print ( people["Billy Bloggs"] ) # get stuff out of dict
Better still, give the attributes names by storing those as a dict as well:
people["Billy Bloggs"] = { 'Age':23, 'Gender':'M', 'Address':'263 Evergreen Tce' }
print ( people["Billy Bloggs"]['Age'] ) # Get billy's age
You can loop through the elements of a dictionary using the following syntax:
>>> mydict = {'a':'Apple', 'b':'Banana', 'c':'Cumquat'}
>>> for key, value in mydict.iteritems():
... print ('Key is :' + key + ' Value is:' + value)
...
Key is :a Value is:Apple
Key is :c Value is:Cumquat
Key is :b Value is:Banana
Note that there is no guarantee on the order of the data. You may insert data in the order A, B, C and get A, C, B back.
Note: The keys of a dict, in this case the person's name, are constrained to be unique. So if you store data to the same name twice, then the first key:value pair will be overwritten.
mydict["a"] = 5
mydict["a"] = 10
print (mydict["a"]) # prints 10
Sidenote: some of your gen*() functions could almost certainly be replaced by random.choice():
import random
first_names = ['Alice','Bob','Charlie','Dick','Eliza']
random_first_name = random.choice(first_names)
Keep data out of your variable names and just store them in a dict.
First, while you haven't shown us the surrounding code, you are probably relying too much on global variables. Rather than trying to create uniquely named arrays for each family member simply do something like this:
Don't really do this (I'll tell you why in a minute)
#houseArray[currentFam][1] is the number of members in the current house.
for currentFam in range(houseArray[currentFam][1]):
family_member_info = []
family_member_info[0] = genSex()
family_member_info[1] = genFirstName()
family_member_info[2] = genAge()
# Pretend 2 is where we are storing the family member information list
houseArray[currentFam][2].append(family_member_info)
A better way
Don't use an array for this sort of thing - it gets very difficult very quickly to tell what is actually stored in which index. Even in your example you have to note that houseArray[currentFam][1] is storing the number of members in the current house.
I would use either a dictionary or a named tuple and store your information in there. That way you can do something like this:
from collections import namedtuple
# Create a class called "household"
# with three fields, "owner", "size" and "members"
household = namedtuple("household", "owner size members")
househould_array = []
# Create some households and put them in the array
household_array.append(household("Family #1", 3, []))
household_array.append(household("Family #2", 1, []))
household_array.append(household("Family #3", 7, []))
# Loop over every household in the household_array
for family in household_array:
# Each `household` namedtulpe's values can be accessed by
# attribute as well as by index number
# family[1] == family.size == 3
# (for Family #1)
for member_number in range(family.size):
# family[2] == family.members == []
# (before we put anything in it)
family.members.append(generate_family_member())
You are mixing program data with variable names. It is okay to call a variable something generic; you do this all the time: e.g. in your for-loop, you use currentFam rather than the name of the family. Asking to uniquely name the array makes (no offense) as much sense as either asking what to name currentFam (it doesn't matter what you name it), or alternatively trying to do:
Andersons[0] = genSex()
Andersons[1] = genFirstName()
Andersons[2] = genAge()
Longs[0] = genSex()
Longs[1] = genFirstName()
Longs[2] = genAge()
Smiths[0] = genSex()
Smiths[1] = genFirstName()
Smiths[2] = genAge()
...
Variables are separate from program data.
You should just name your array person, and store it with other arrays. Even better would be to define a class Person(object): ..., so you could do things like x.name and x.age, but you don't need to do that. For example:
class Person(object):
def __init__(self, **kw):
self.data = kw
self.__dict__.update(kw)
def __repr__(self):
return str('Person(**{})'.format(self.data))
__str__ = __repr__
M = Person.M = 'm'
F = Person.F = 'f'
ALL_PEOPLE = set()
for ...:
person = Person(name=..., age=..., sex=...)
people.add(person)
Then to find people:
def findPeople(name=None, age=None, custom=set()):
matchers = custom
if name!=None:
matchers.add(lambda x:name.lower() in x.name.lower())
if age!=None:
matchers.add(lambda x:age==x.age)
return set(p for p in ALL_PEOPLE if all(m(p) for m in matchers))
Demo:
ALL_PEOPLE = set([
Person(name='Alex', age=5, sex=M),
Person(name='Alexander', age=33, sex=M),
Person(name='Alexa', age=21, sex=F)
])
>>> pprint.pprint( findPeople(name='alex', custom={lambda p: p.age>10}) )
{Person(**{'age': 33, 'name': 'Alexander', 'sex': 'm'}),
Person(**{'age': 21, 'name': 'Alexa', 'sex': 'f'})}
Wow, I really enjoyed reading all of the other answers.
So many great suggestions including, but not limited to:
#Sean Vieira suggests named-tuples -- an excellent, light-weight choice;
#ninjagecko uses a neat trick to dynamically assign instance attributes;
#Li-aung Yip mentions using the built-in sqlite3 module.
Much if not all of what's here has already been suggested.
If nothing else I hope this answer is an introduction to what classes may provide beyond what is provided by other data-structures.
Caveat: If performance is a huge concern, modeling each entity as a class might be overkill.
from __future__ import division, print_function
class Town(object):
def __init__(self, name=None, country=None, area_km2=0, population=0):
self.name = name
self.area_km2 = area_km2
self.area_mi2 = self.area_km2 * 0.38610217499077215
self.population = population
self.households = []
#property
def total_households(self):
return len(self.households)
#property
def population_density_per_km2(self):
try:
return self.population / self.area_km2
except ZeroDivisionError:
return 0
#property
def population_density_per_mi2(self):
try:
return self.population / self.area_mi2
except ZeroDivisionError:
return 0
class Household(object):
def __init__(self, primary_lang='Esperanto'):
self.primary_lang = primary_lang
self.members = []
#property
def total_members(self):
return len(self.members)
class Person(object):
def __init__(self, age=0, gender=None, first_name=None):
self.age = age
self.gender = gender
self.first_name = first_name
if __name__ == '__main__':
londontown = Town(name='London',
country='UK',
area_km2=1572,
population=7753600)
print(londontown.population_density_per_km2)
print(londontown.population_density_per_mi2)
a_household = Household()
a_household.members.append(
Person(age=10, gender='m', first_name='john'),
)
a_household.members.append(
Person(age=10, gender='f', first_name='jane')
)
londontown.households.append(a_household)
print(londontown.total_households)
print(a_household.total_members)

Categories