Learn Python, exercise 6.7 - python

I'm reading Lutz & Ascher - Learn Python and I found this as a solution to one of the exercises:
class Lunch:
def __init__(self):
self.cust = Customer()
self.empl = Employee()
def order(self, foodName):
# start a Customer order simulation
self.cust.placeOrder(foodName, self.empl)
def result(self):
# ask the Customer what kind of Food it has
self.cust.printFood()
class Customer:
def __init__(self):
# initialize my food to None
self.food = None
def placeOrder(self, foodName, employee):
# place order with an Employee
self.food = employee.takeOrder(foodName)
def printFood(self):
# print the name of my food
print self.food.name
class Employee:
def takeOrder(self, foodName):
# return a Food, with requested name
return Food(foodName)
class Food:
def __init__(self, name):
# store food name
self.name = name
if __name__ == '__main__':
x = Lunch()
x.order('burritos')
x.result()
x.order('pizza')
x.result()`
What I don't understand is how the definition of the method placeOrder inside the customer class works, more specifically, there is no class employee (just Employee) whose method placeOrder could be used.

def placeOrder(self, foodName, employee):
# place order with an Employee
self.food = employee.takeOrder(foodName)
you may need to read a little bit about object oriented programming, and dynamic typing to grasp this. So basically, employee is an argument which will be passed at runtime, its type will be determined after the call to placeOrder. if you call PlaceOrder and put an instance of Employee or any class that has method takeOrder(), it will work. Imho, you should try to code an example from the beginning and test out what you learn, it will help you learn Python faster
`

Related

inner classes of python loop referencing the same memory and assigning the last referenced variable

So im learning python and am trying to build a python file with inner classes and im trying to populate subjects as an array inside a class. However, it seems to be pointing to the same memory even though i am using a dictionary to ensure it does not happen. Is there any way for me to fix this issue?
I am trying to assign 2 subjects to my student. However, when accessing the name of the subjects, it is returning me the same subject which is located at the 1st index.
I'm used to java where i can just create a new instance of a class using the new tag.
class Student:
class Subject:
def __init__(self):
self.name = ''
def __init__(self):
self.name = ''
self.subject = self.Subject()
self.courses = {}
def test():
courses = ['Subject A', 'Subject B']
student = Student()
student.name = 'apple'
for i, course in enumerate(courses):
student.courses[i] = student.subject
student.courses[i].name = course
print(student.courses[0].name)
print(student.courses[1].name)
test()
Thank you very much for the help! I am struggling to get a grasp of this.
The class is an object itself, so when you run
student.courses[i] = student.Subject
you are not creating an instance of the class, you are setting the "static" class.
When you override the name here, you are doing it for the shared class.
student.courses[i].name = course
A fix would be:
student.courses[i] = student.Subject()
Here is a more idiomatic python way of writing your program, just something to ponder. In particular, I rarely see inner classes referenced via self.XXX, but as you can see it works.
class Student:
class Subject:
def __init__(self, name=None):
self.name = name
def __str__(self):
return 'subject is "%s"' % self.name
def __init__(self, name=None):
self.name = name
self.courses = []
def addcourse(self, name):
self.courses.append(Student.Subject(name))
def test():
subjects = []
courses = ["Subject A", "Subject B"]
student = Student(name="Student A")
for course in courses:
student.addcourse(course)
print(student.courses[0])
print(student.courses[1])

What happens when python imports a class with composition?

Despite of this question being answered:
How to import a class with composition from a module?
I still don't understand the functioning of importing composite classes in python.
The following is a kind of long code but very basic. In a file called example.py there are three classes, Employee, Car and Company. Company calls Employee and employee calls Car.
Employee has a static method which does not work, simply returns a variable that does not exists.
example.py:
class Employee():
def __init__(self,name):
self.name = name
def add_employee_atribute(self):
if ' ' in self.name:
self.completename='created with method class: ^' + self.name.title()
def upperit(self):
return self.name.upper()
#staticmethod
def empstatic(myname):
return myname+myname
#staticmethod
def empstatic_wrong():
# this is wrong
return ERROR
def add_car(self,make):
self.car = Car(make)
class Car():
def __init__(self, make):
self.car = make
class Company():
def __init__(self,name,people):
self.name=name
self.employees=[]
for person in people:
self.employees.append(Employee(person))
def get_all_employess(self):
return 'These are all: ' + ', '.join([e.name for e in self.employees])
def get_employee(self,name):
for e in self.employees:
if e.name == name:
return e
Now if I want to use Company in a file I go:
work.py:
from example import Company
C = Company('kodak',['peter','john smith'])
C.employees[1].add_employee_atribute()
C.get_all_employess()
peter = C.get_employee('peter')
peter.add_car('BMW')
This all works.
Python seems to evaluate the imported code (Company) in work.py in a way that realises that Company needs Employee and Employee needs Car. Very clever. So it looks like python looks for the classes when they are called. Nevertheless python does not realise that the variable 'ERROR' does not exist.
So what puzzles me is the fact that python is able to distinguish, look for the composite classes but not for the variables.
Some explanation about this?

Creating a list of class instances

I'm trying to make a simple library system as part of an intro to OOP in Python. I became stuck trying to fit books into my library class. I have made a first simple class, called Book, which makes books, can show their ID, name and price etc.
Now im trying to create the actual class called Library. I want to make a list of all the books in the library with their ID, cost and name. Here I encountered an isseu. I have no idea how to add the instances of the class Book to the list I made in Library, my code can be found down here.
class Library(object):
def __init__(self, book):
self.book = book
def add_item(self, book):
mylist.append(book)
return mylist
if __name__ == '__main__':
booklist = []
Book1 = Book(1, 'Bookname1', "$30")
Book2 = Book(2, 'Bookname2', "$10")
Book1.show()
Book1.get_attribute_string()
and the code for the books, which I would rather keep the same. Ofcourse im open to suggestions, but im not that well versed in OOP in Python yet so don't suggest things to complicated! Thanks.
class Book(object):
def __init__(self, ID, name, price):
self.ID = ID
self.name = name
self.price = price
def show(self):
print(self.ID, self.name, self.price)
def get_attribute_string(self):
print(str(self.ID) + '_' + str(self.name) + '_' + str(self.price))
def get_id(self):
print(self.ID)
def get_name(self):
print(self.name)
def get_price(self):
print(self.price)
The class Library needs an attribute e.g. books. In this list, all books are stored. You need to define it in the __init__method, so you can have multiple libraries with different books.
class Library:
def __init__():
self.books = []
def add_book(book):
self.books.append(book)
def get_books(self):
return self.books
def show_books(self):
# This is one possible example how you can print all books
for book in self.books:
print(str(book))
class Book:
pass # Your Book code
def __str__(self):
return f"{self.name}, {self.id}, {self.price}"
# Returns a string with all attributes of the string.
# Note that is a f-string, but you can use any string
# This function is called, when you call str(book1)
if __name__ == "__main__":
book1 = Book()
book2 = Book()
lib = Library()
lib.add_book(book1)
lib.add_book(book2)
lib.show_books()
all_books = lib.get_books()
also_all_books = lib.books
Your library class should probably contain a collection of books (or something more specific, depends on the details of your task). Here is an example:
class Library(object):
def __init__(self, book):
# collection of library books. This is the library state
self.books = tuple()
def add_item(self, book):
# add an item to the collection
self.books += (book, )
return self.books
I preferred to use an immutable collection so that others can't change the library state outside the class. But you can use a list. You just have to be careful.

Initialising nested classes in Python

Let's say I want to create a class 'House' that has some attributes of its own, but also has a (nested?) 'Resident' class which has some attributes and has a mandatory attribute 'surname'. A house instance may exist though without any residents. How can create this so that I can eventually do the following?
myhouse = House()
residentX = myhouse.resident('Smith')
Currently I set this up as a nested class but run into trouble when I try and initialise myhouse given that it is requiring a surname at this point for the nested Resident class (which I don't necessarily have at this point)
class House:
def __init__(self):
self.someattribute = <someattribute>
self.resident = self.Resident()
class Resident:
def __init__(self, surname):
self.surname = surname
I know I can restructure the code to not use nested classes and then explicitly tie any resident to a house in my code. However, I would like to use the dot notation here (myhouse.resident) to automatically tie a resident to a house.
Also, I understand that nested classes in python are somewhat frowned upon - I'm open to suggestions on how to do the above in a more pythonic manner.
I would break out the Resident class and use a property/setter for .resident
Like this:
class House:
def __init__(self):
self.someattribute = <someattribute>
self._resident = None
#property
def resident(self):
return self._resident
#resident.setter
def resident(self, surname):
r = Resident(surname)
self._resident = r
class Resident:
def __init__(self, surname):
self.surname = surname
However, if you want .resident to be callable but also want to track the house's residents, you can still break out the Resident class, and use:
class House:
def __init__(self):
self.someattribute = <someattribute>
self.residents = []
def resident(self, surname):
'''
Add a resident to the house
'''
r = Resident(surname)
self.residents.append(r)
return r
class Resident:
def __init__(self, surname):
self.surname = surname

TypeError in Python 3.x

I have no idea what is wrong! This is a very simple program and I have done a lot head banging! Please someone enlighten me!
This a lab problem from the CSE 111 - Programming Language II course. They teach Java at the university and the code I wrote in Java works fine.
I just have to create a Student class with some fields to hold the basic information about a student with methods to get and set the attributes. Then create an instance of that class and tryout the methods.
But every time I run this program the following error occurs:
TypeError: set_name() takes exactly 1 positional argument (2 given)
Here is the code I wrote.
class Student:
'''Student class'''
name = None
id = 0
address = None
cgpa = None
def get_name():
return name
def set_name(n):
name = n
def get_id():
return id
def set_id(i):
id = i
def get_address():
return address
def set_address(a):
address = a
def get_cgpa():
return cgpa
def set_cgpa(c):
cgpa = c
#An object of Student class
jack = Student()
jack.set_name('jacky')
print(jack.get_name())
You're not accepting a reference to your instance as the first argument to that method, i.e. your set_name() should be written:
def set_name(self, n):
self.name = n
This is somewhat different from other languages where there is a built-in keyword (such as this) that refers to the current object. Python passes that reference explicitly, as an argument to the method.
All your other methods must be modified similarly.
Note that just setting name = n sets a local variable name which goes away when the method ends; it does not set anything on the instance. You have to explicitly set self.name if you want an instance attribute.
Also, and this is a matter of style, but you do not usually write set and get methods in Python. It is normal practice to set and get attributes directly. If you want to do validation of values, use a property instead. So basically, none of your methods are actually necessary in good style.
However, you don't have an __init__() method. Usually you would pass the desired attributes of the instance when instantiating the class and save these on the instance.
class Student:
def __init__(self, name, id, address, cgpa):
self.name = name
self.id = id
self.address = address
self.cgpa = cgpa
herman = Student("Herman Munster", 12345, "1313 Mockingbird Lane", 4.0)
Try this:
import sys
class Student:
'''Student class'''
self.name = None
self.id = 0
self.address = None
self.cgpa = None
def get_name(self):
return self.name
def set_name(self, n):
self.name = n
def get_id(self):
return self.id
def set_id(self, i):
self.id = i
def get_address(self):
return self.address
def set_address(self, a):
self.address = a
def get_cgpa(self):
return self.cgpa
def set_cgpa(self, c):
self.cgpa = c
You need to pass self as the first argument to each member function of the class. Member variables must then be referred to with self, i.e. self.name. Furthermore, you may wish to include an __init__() function; this serves usually to initialize any member variables, and is called at the instantiation of the class.
Take a look at the Python documentation here for some examples on well-formed classes: http://docs.python.org/tutorial/classes.html#random-remarks
In Python, you need to pass in self for each of your member functions. You also need to reference class variables as self.x, if you want them to take an effect.
Here are a couple examples that you need to apply to the rest of your code.
def set_name(self, n):
self.name = n
def get_cgpa(self):
return self.cgpa
There is some explanation for why this is the case in the documentation.
This is because first argument of methods is self - the class instance.
See What is the purpose of self?
and http://docs.python.org/tutorial/classes.html#class-objects

Categories

Resources