Im stuck with my Python code, could someone take a look? [closed] - python

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am currently trying to teach myself python by reading through "Python for absolute beginners" by Michael Dawson. I am currently working through the challenges in chapter 9, largely to really get a grip on how classes actually interact with each other. I am attempting to write a (very) simple text based adventure game where the user travels between various locations. However the way that I am trying is not doing what I want it to. Here is the code:
*Disclaimer - I am well aware this probably looks like garbage, I don't entirely understand what I've written myself and its aim is just to give me an understanding of class interactions.
# classes module for my basic adventure game
import random
# the locations that the player will travel to
class Location(object):
"""Locations that the adventurer can travel to"""
def __init__(self, name, description):
self.name = name
self.description = description
class Location_list(object):
def __init__(self):
self.locations = []
def __str__ (self):
if self.locations:
rep = ""
for location in self.locations:
rep += str(location)
else:
rep = "<empty>"
return rep
def clear(self):
self.locations = []
def add(self, location):
self.locations.append(location)
def rand_location(self):
x = random.choice(self.locations)
print("You step out of the magical portal, it has taken you to", x, "!")
# The player
class Adventurer(object):
"""An adventurer who travels to different locations within a mystical world"""
def __init__(self, name):
self.name = name
def travel(self):
Location_list.rand_location
print("You step into the magic portal")
loc1 = Location("London", "The capital of the UK")
loc2 = Location("Paris", "The capital city of France")
loc3 = Location("Berlin", "The capital city of Germany")
location_list = Location_list
player = Adventurer(input("What is your name traveller?"))
question = input("do you want to travel now?")
if question == "yes":
player.travel()
else: print("No journey for you bellend")
input("press enter to exit")
This is my code so far. Effectively what I wanted to do was have a class of locations and a class that created a list of those locations. I would then have a method on the location class that called a method on the location list class to return a random location from the list. As far as I can tell the problem i'm having is that the list isn't actually being created. The code i used for this i actually stole from earlier in the chapter as i thought it would do what i wanted it to do - code in question:
class Location_list(object):
def __init__(self):
self.locations = []
def __str__ (self):
if self.locations:
rep = ""
for location in self.locations:
rep += str(location)
The problem is that i dont actually get anything back from calling the players travel method beyond calling the print parts of it.
So firstly could someone please help me sort out what i've already got so that that the location list actually creates a list of location objects, and then the method randomly selects from this list
And secondly, if my code is, as i suspect, barking up the completely wrong tree. Could someone show me a way of creating a class that is a list of other objects.

I mentioned in a comment why this is a bad question. Just to emphasize, I'll point you again to https://stackoverflow.com/help/mcve.
You don't describe your problem, but mention you suspect some list not getting created. Indeed you have a line like this:
location_list = Location_list
This line has a mistake! You should change it to:
location_list = Location_list()
The second version creates an instance of the Location_list class and assigns it to a variable. This is what you want. On the other hand the first version gives another name to the Location_list class. So then you could say location_list() to create an instance of the same class. But that is not what you want.
I didn't read through the whole code, just looked at uses of this list, but here's another mistake:
def travel(self):
Location_list.rand_location
print("You step into the magic portal")
Location_list is a class. Location_list.rand_location is an unbound method of this class. In that line you just refer to this method, but you don't even call it. It's like writing 15 on a line. Valid code, but doesn't do anything.
Instead you want to refer to an instance of the class. If you've fixed the first mistake you can write location_list. (note the lowercase l instead of L.) And you want to call the rand_location method. So you need to write:
def travel(self):
location_list.rand_location()
print("You step into the magic portal")

First you have to properly instantiate Location_list with location_list = Location_list() Then you have to add the location instances to your location_list instance. So after that
add
location_list.add(loc1)
location_list.add(loc2)
location_list.add(loc3)
Also to print these locations you should add a __str__ method to your Location class else str(location) won't give you the name of the location:
def __str__ (self):
if self.name:
return str(self.name)

Related

What I can do better in this class? Python class refactoring [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I have this code with some classes and the inheritance tree. What I can do better in this code? If we look in the direction of designing classes.
CAR_TYPES = {
'Car': 'Car',
'Truck': 'Truck',
'SpecMachine': 'SpecMachine'
}
class CarBase:
def __init__(self, brand, photo_file_name, carrying):
self.car_type = None
self.photo_file_name = photo_file_name
self.brand = brand
self.carrying = carrying
def get_photo_file_ext(self):
return self.photo_file_name.split(".")[-1]
def __str__(self):
return f"Car type: {self.car_type} | Brand: {self.brand} | Carrying: {self.carrying}"
class Truck(CarBase):
def __init__(self, photo_file_name, brand, carrying, body_lwh):
super().__init__(photo_file_name, brand, carrying)
self.car_type = CAR_TYPES['Truck']
self.body_lwh = body_lwh
self.body_length = 0
self.body_width = 0
self.body_height = 0
self.body_volume = 0
if body_lwh:
self._set_lwh()
self._set_body_volume()
def __str__(self):
return f"{super().__str__()} | Length: {self.body_length} | Width: {self.body_width}, " \
f"| Height {self.body_height}, | Volume: {self.body_volume}"
def _set_lwh(self):
try:
self.body_length, self.body_width, self.body_height = map(float, self.body_lwh.split('x'))
except ValueError:
self.body_length, self.body_width, self.body_height = 0.0, 0.0, 0.0
print("Value Error. Check your values and try again!")
def _get_body_volume(self):
return self.body_length * self.body_width * self.body_height
def _set_body_volume(self):
self.body_volume = self._get_body_volume()
class Car(CarBase):
def __init__(self, photo_file_name, brand, carrying, passenger_seats_count):
super().__init__(photo_file_name, brand, carrying)
self.car_type = CAR_TYPES['Car']
self.passenger_seats_count = passenger_seats_count
def __str__(self):
return f"{super().__str__()} | Passenger seats count: {self.passenger_seats_count}"
class SpecMachine(CarBase):
def __init__(self, photo_file_name, brand, carrying, extra):
super().__init__(photo_file_name, brand, carrying)
self.car_type = CAR_TYPES['SpecMachine']
self.extra = extra
def __str__(self):
return f"{super().__str__()} | Extra: {self.extra}"
I want to make this code more readable and more scalable, but I don't have any experience in this field and I want to learn it.
For example, what I can do with car_type variable? I tried to put car_type to CarBase class, but I don't know, how I can assign it later and to make it right from the design side
Abstracting to make things more readable actually has an opposite effect most of the time.
First off, you're doing really well here. You're functions/methods are all small - only a few lines. Which means its much easier to absorb what that method does then go back to the rest of the code. Plus, your names are pretty descriptive - they don't use variable types in the name (a good thing!!) and they don't use acronyms. The only abbreviation you used was lwh which is ... pretty easy to grasp.
It looks like you're using a dictionary of terms for CAR_TYPE and if this is a completely self contained system that is plenty fine. If this is code that others will be working on as well, you might use Litterals for this instead:
class CarType():
MAZARATI = "Mazarati"
FERRARI = "Ferrari"
SPORTS_CAR = "Performance"
ect ect. If you define Litterals like this in a class, outside the __init__ constructor, you can access them directly without having to instantiate a class. These become module constants, and you could reference them like:
import CarType
if car_type == CarType.SPORTS_CAR:
print("Zooooomy!")
which can look more professional, plus also Intellisense will pick these up and provide them in any decent IDE, where as a dictionary will not show its available Keys.
Overall - I don't think there is much you have to worry about here for refactoring. The main idea with refactoring is to make your code DRY - Don't Repeat Yourself -- and you pretty much are not repeating yourself. The other side of refactoring is to make the code more Readable - As a general rule any given dev of the same or more experience as you (and to some extent, less!) should be able to simply read your code like it was a book (hence the praise above that your methods are small, and your method names and variable names are actually readable and dont make me have to puzzle it out)
Don't worry too much about refactoring if most of your stuff is 1 or 2 lines. As my first line here said, there is a point where too much refactoring too much can actually make it worse. If I have to jump to 10 different methods in order to figure out whats happening it is more of a detriment than anything else (This is also why some opinions are that your method that are called inside another should always be defined after and below, so you are reading down the page like you would read a book. This is just an opinion however - some people disagree and think it should be the opposite - kinda sometimes depends on the language)
Overall - if this came across my docket as a Pull Request to review with one of my jr devs I'd be very pleased!
Some stuff you might try for future implementations:
Any of your attribtues that are default set to None, you could also default with a kwarg.
def init(self, brand, photo_file_name, carrying, car_type=None):
self.car_type = car_type
this way when someone instantiates the car they can go ahead and set the car_type during the instantiation instead of having to use a second line.
If you get more than 4 or 5 properties to pass in, you might consider using dictionaries (if this is internal) or converting some or all them all to kwargs so that they have to be tagged in the instantiation (car_base = CarBase(brand=A, photo_file_name=B.jpg, ...) which when you have more than a couple properties can help clear things up.
Great work! keep it up!
Lots of things depend on your design goals, so we would need to know what your car classes need to be doing for their clients to comment more on their design, but I can comment on your use of types.
First, the class name CarBase suggests that it is not meant to be instantiated, that it is just a base for your derived classes. If this is the intent of your design, i would suggest looking into python's abc.abstractclass so that you can make this an abstract class.
Second, to answer your question about the car_type variable: I will claim that you don't need at all, and even that you shouldn't have it. Usually, the purpose of inheritance is so that your clients don't need to care what your type is, they can just know that you are some kind of car, and that you will take care of how that matters for your specific type. This is called polymorphism.

How can you automatically change the display of an object? [Python]

Suppose you have an object called e_book that simulate an electronic book. Functionality to be achieved is that every time you execute "print(e_book)", the printed display will behave like flipping through the pages.
For example, execute "print(e_book)" the first time:
"All happy families are alike."
execute "print(e_book)" the second time displayed content will change:
"Each unhappy family is unhappy in its own way."
and the third time it will change again, and so on.
To sum up, I want to control the string display of an object by simply calling "print(object_name)". Is this achievable?
To control the display of an objet, override the repr method in your class
def __repr__(self):
return 'whatever'
You can also redefine the str method.
class EBOOK():
def __init__(self):
self.pages = list()
self.seenPages = -1
def addPage(self, data):
self.pages.append(data)
def __repr__(self):
self.seenPages += 1
return str(self.pages[self.seenPages])
e_book = EBOOK()
e_book.addPage("All happy families are alike.")
e_book.addPage("Each unhappy family is unhappy in its own way.")
print(e_book)
print(e_book)
should produce:
All happy families are alike.
Each unhappy family is unhappy in its own way.

Iterating over "Self" Variable in Class (Attribute Error)

So I'm not sure if the title even makes sense but basically I have a class that keeps track of friends (like an address book), and I'm at the part where I need to create a function that adds friends to a set of existing names. So I've got the beginning of my code as:
class SocialAddressBook:
def __init__(self):
self.book= []
def addName(self, name, address):
self.book.append([name, address, set()])
"""Adds name to address book, with address and no friends"""
which runs fine. and then the part that gives me 'list' object has no attribute 'list' error:
def addFriend(self, name, friend):
for k in range(len(self.book.list[k])):
if self.book[k][0] == name:
my idea was to iterate over a list and add onto the existing list, but syntax is definitely throwing me for a loop (no pun intended) and I'm not sure how I should go about this now.
some test code:
a.addFriend('Fred', 'Barb'); a.addFriend('Fred', 'Sue')
a.addFriend('Barb', 'Jane'); a.addFriend('Jane', 'Emma')
a.addFriend('Jane', 'Mary'); a.addFriend('Emma', 'Lisa')
Thank you!
Your for loop in your addFriend method is incorrect. Specifically, self.book.list[k].
First off, self.book is a list, and lists do not have a list property. Second, you can't use k there as it does not yet exist.
If you want to loop over a list, why not just do that? You don't need to use range().
for book in self.book:
if book[0] == name:
pass
If you can, your solution should be implemented using a dictionary instead of a list to improve performance of your address book. Adding names is trivial, and other than the error checking involved, adding friends is also trivial. For a more complete solution, a remove_name method has been added showing how to correctly remove someone.
import collections
def main():
book = SocialAddressBook()
# Add people to the address book.
book.add_name('Fred', 'fred#gmail.com')
book.add_name('Barb', 'barb#gmail.com')
book.add_name('Jane', 'jane#gmail.com')
book.add_name('Emma', 'emma#gmail.com')
book.add_name('Sue', 'sue#gmail.com')
book.add_name('Mary', 'mary#gmail.com')
book.add_name('Lisa', 'lisa#gmail.com')
# Add friends connections in the address book.
book.add_friend('Fred', 'Barb')
book.add_friend('Fred', 'Sue')
book.add_friend('Barb', 'Jane')
book.add_friend('Jane', 'Emma')
book.add_friend('Jane', 'Mary')
book.add_friend('Emma', 'Lisa')
class SocialAddressBook:
def __init__(self):
self.__book = {}
def add_name(self, name, address):
"""Adds name to address book with address and no friends."""
self.__book[name] = BookEntry(address, set())
def add_friend(self, name, friend):
"""Adds a friend to the person referenced by name."""
if friend not in self.__book:
raise ValueError(f'{friend!r} is not in the address book yet')
self.__book[name].friends.add(friend)
def remove_name(self, name):
"""Completely delete someone from the address book."""
del self.__book[name]
for book_entry in self.__book.values():
book_entry.friends.discard(name)
BookEntry = collections.namedtuple('BookEntry', 'address, friends')
if __name__ == '__main__':
main()
Please read PEP 8 -- Style Guide for Python Code soon if possible. Code written in Python should conform to the style guide to promote maintainability and to encourage others to read your code and answer your questions. In the future, please create a Minimal, Complete, and Verifiable example of your problem before asking your question.

Restrict type of elements of list

I am new to python, but I have some knowledge of Java. Here I have my first python code :
class Movie(object):
def __init__(self, name, year):
self.name = name
self.year = year
def tostring(self):
return "%s (%s)" % (self.name,self.year)
def __str__(self):
return self.tostring()
class MoviesCollection(object):
def __init__(self):
self.colection = []
def add_movie(self, movie):
self.colection.append(movie)
def __iter__(self):
return iter(self.colection)
def __str__(self):
return '\n'.join(map(str, self.colection))
filmy = MoviesCollection()
a = Movie('A', 256)
b = Movie('B', 512)
c = Movie('C', 1024)
filmy.add_movie(a)
filmy.add_movie(b)
filmy.add_movie(c)
filmy.add_movie(c)
filmy.add_movie(c)
filmy.add_movie(c)
#filmy.add_movie('Aloha')
for m in filmy:
print m.tostring()
print filmy
Now this works well, but as I uncoment the line from the bottom I will have some runtime errors as I am calling a tostring() method on str type object. And here is the question. How can I prevent inserting such object of different type than Movie into colection ? The Java guy in me would make that list colection like this:
List<Movie>
Also I could put a condition there to add_movie function like this :
if isinstance(movie, Movie):
but isn't there any other way ?
In Python - you are expected to behave appropriately and don't do things that don't make sense. This is part of the flexibility of a language that was designed for teaching and scientists, and doesn't put too much emphasis on types, or "object contracts" and other such fluff.
The way you would write your code by taking all the "Java" out of it is:
class Movie(object):
""" A movie, thing to spend a few hours
watching, then a few hours hating yourself
over watching it. """
def __init__(self, name, year):
self.name = name
self.year = year
def __str__(self):
return '{} ({})'.format(self.name, self.year)
movie_collection = [Movie('Titanic', 1997), Movie('Showgirls', 1995)]
for movie in movie_collection:
print(movie)
In the Python world Java is sometimes affectionately referred to as a "bondage" language. There are two fundamentally different programming language philosophies: one group seeks to make it harder to write bad programs (Java), the other strives to make it easier to write good programs (Python). Neither is completely successful, because it is possible to write bad programs in any language.
Overall, in Python we prefer to write our code so that only Movie objects are added to the list, rather than take the time to verify that every element put on the list is a Movie. Unit tests can be used to verify this in a given number of cases, but this will never give you the assurance you seek.
Type checking of the nature you suggest is possible, but generally held to be unnecessary or even "unPythonic".

Creating objects during runtime in Python

I have a problem grasping the OOP concept when it comes to creating objects during runtime. All the educational code that I have looked into yet defines specific variables e.g. 'Bob' and assigns them to a new object instance. Bob = Person()
What I have trouble understanding now is how I would design a model that creates a new object during runtime? I'm aware that my phrasing is probably faulty since all objects are generated during runtime but what I mean is that if I were to start my application in a terminal or UI how would I create new objects and manage them. I can't really define new variable names on the fly right?
An example application where I run into this design issue would be a database storing people. The user gets a terminal menu which allows him to create a new user and assign a name, salary, position. How would you instantiate that object and later call it if you want to manage it, call functions, etc.? What's the design pattern here?
Please excuse my poor understanding of the OPP model. I'm currently reading up on classes and OOP but I feel like I need to understand what my error is here before moving on. Please let me know if there is anything I should clarify.
Things like lists or dictionaries are great for storing dynamically generated sets of values/objects:
class Person(object):
def __init__(self, name):
self.name = name
def __repr__(self):
print "A person named %s" % self.name
people = {}
while True:
print "Enter a name:",
a_name = raw_input()
if a_name == 'done':
break
people[a_name] = Person(a_name)
print "I made a new Person object. The person's name is %s." % a_name
print repr(people)
You don't store each object with a variable name. Variable names are for the convenience of a programmer.
If you want a collection of objects, you use just that - a collection. Use either a list or a dictionary containing object instances, referenced by index or key respectively.
So for example, if each employee has an employee number, you might keep them in a dictionary with the employee number as a key.
For your example, you want to use a model abstraction.
If Person is a model class, you could simply do:
person = new Person()
person.name = "Bob"
person.email = "bob#aol.com"
person.save() # this line will write to the persistent datastore (database, flat files, etc)
and then in another session, you could:
person = Person.get_by_email("bob#aol.com") # assuming you had a classmethod called 'get_by_email'
I'll try to answer as best I can here:
What you're asking about is variable variable names - this isn't in Python. (I think it's in VB.Net but don't hold me to that)
The user gets a terminal menu which allows him to create a new user and assign a name, salary, position. How would you instantiate that object and later call it if you want to manage it, call functions, etc.? What's the design pattern here?
This is how I'd add a new person (Mickey-mouse example):
# Looping until we get a "fin" message
while True:
print "Enter name, or "fin" to finish:"
new_name = raw_input()
if new_name == "fin":
break
print "Enter salary:"
new_salary = raw_input()
print "Enter position:"
new_pos = raw_input()
# Dummy database - the insert method would post this customer to the database
cnn = db.connect()
insert(cnn, new_name, new_salary, new_pos)
cnn.commit()
cnn.close()
Ok, so you want to now get a person from the database.
while True:
print "Enter name of employee, or "fin" to finish:"
emp_name = raw_input()
if emp_name == "fin":
break
# Like above, the "select_employee" would retreive someone from a database
cnn = db.connect()
person = select_employee(cnn, emp_name)
cnn.close()
# Person is now a variable, holding the person you specified:
print(person.name)
print(person.salary)
print(person.position)
# It's up to you from here what you want to do
This is just a basic, rough example, but I think you get what I mean.
Also, as you can see, I didn't use a class here. A class for something like this would pretty much always be a better idea, but this was just to demonstrate how you'd change and use a variable during runtime.
You would never do Bob = Person() in a real program. Any example that shows that is arguably a bad example; it is essentially hard-coding. You will more often (in real code) do person = Person(id, name) or something like that, to construct the object using data you obtained elsewhere (read from a file, received interactively from a user, etc.). Even better would be something like employee = Person(id, name).

Categories