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.
Related
This question already has answers here:
How to dynamically load a Python class
(13 answers)
Closed 11 months ago.
I'm new in python, and need some help here:
The user enters an identifer (project name, client name) through the GUI or controle screen.
This text entered by the user should be the identifier that automatically instantiates a Class:
identifier = Class()
Question: how can I do that in python? (so far as I have seen in videos, every instantiation was done directly from a variable typed inside the code...)
You can use a bunch of if statements to decide which class to instantiate
user_input = input()
if user_input == 'myClass':
identifier = myClass()
elif user_input == 'anotherClass':
identifier = anotherClass()
...
You don't want to just blindly accept any input from the user because of security issues. There is eval and exec, but you would be basically giving the user free reign to also run whatever code they want by using those.
If you have more than a few classes, then instead of if statements you can use a dictionary like so.
inputs_to_classes = { 'myClass': myClass, 'anotherClass': anotherClass }
user_input = input('Enter a class to instantiate: ').lower()
identifier = inputs_to_classes[user_input]()
Or in Python 3.10+ you could use a match expression, but I don't currently have access to Python 3.10.
the correct answer in this case is: don't do this. (and yes, it's possible to do this, but again, don't do this)
Essentially, your user should not even need to be made aware of what variables are, and should absolutely not be controlling what variable names are created in your code dynamically for your objects of these classes.
So, one actual solution that bypasses this issue is to just use a dictionary. take a string from user, that's a key in a dictionary. initialize your class against the key in the dictionary, and just use the key to access the class.
Another, perhaps better solution, is to just have your class take an extra name attribute. when you need to display the name the user entered for the class, you access it on the attribute. Then you do not even need to use a dictionary, you just create the class with your own variable name internally, but always display the user entered name from it's attribute. This separates concerns between what the user knows and what the programmer should deal with.
# Recommended solution
class Project:
def __init__(self, name, other_params):
self.name = name
self.other_params = other_params
def some_method(self, extra_args):
pass
class Client:
def __init__(self, name, other_params):
self.name = name
self.other_params = other_params
def some_method(self, extra_args):
pass
# these can come from your gui instead of input, doesn't matter
project_user_input = input("enter project name: ")
# these can come from your gui instead of input, doesn't matter
client_user_input = input("enter client name: ")
# make the actual objects, your variable names are internal to you,
# and the names themselves should not be tied to business logic.
# use the .name method to access the user-facing names.
project_obj = Project(name=project_user_input, other_params=42)
client_obj = Client(name=client_user_input, other_params=100)
# you can always access user facing names as necessary.
print(f"the project has the name: {project_obj.name}")
print(f"the client has the name: {client_obj.name}")
Decide on gui framework
Learn gui framework
Come back with questions regarding that framework
Right now I am learning Python and struggling with a few concepts of OOP, one of that being how difficult it is (to me) to dynamically initialize class instances and assign them to a dynamically generated variable name and why I am reading that I shouldn't do that in the first place.
In most threads with a similar direction, the answer seems to be that it is un-Pythonic to do that.
For example generating variable names on fly in python
Could someone please elaborate?
Take the typical OOP learning case:
LOE = ["graham", "eric", "terry_G", "terry_J", "john", "carol"]
class Employee():
def __init__(self, name, job="comedian"):
self.name = name
self.job = job
Why is it better to do this:
employees = []
for name in LOE:
emp = Employee(name)
employees.append(emp)
and then
for emp in employees:
if emp.name == "eric":
print(emp.job)
instead of this
for name in LOE:
globals()[name] = Employee(name)
and
print(eric.job)
Thanks!
If you dynamically generate variable names, you don't know what names exist, and you can't use them in code.
globals()[some_unknown_name] = Foo()
Well, now what? You can't safely do this:
eric.bar()
Because you don't know whether eric exists. You'll end up having to test for eric's existence using dictionaries/lists anyway:
if 'eric' in globals(): ...
So just store your objects in a dictionary or list to begin with:
people = {}
people['eric'] = Foo()
This way you can also safely iterate one data structure to access all your grouped objects without needing to sort them from other global variables.
globals() gives you a dict which you can put names into. But you can equally make your own dict and put the names there.
So it comes down to the idea of "namespaces," that is the concept of isolating similar things into separate data structures.
You should do this:
employees = {}
employees['alice'] = ...
employees['bob'] = ...
employees['chuck'] = ...
Now if you have another part of your program where you describe parts of a drill, you can do this:
drill['chuck'] = ...
And you won't have a name collision with Chuck the person. If everything were global, you would have a problem. Chuck could even lose his job.
I'm teaching myself Python (2.7, no previous coding experience) and I've just started dealing with classes and OOP concepts. As an exercise, I'm trying to code a very simple address book. I think I managed to understand the basics of classes and instances, but what I'm finding hard to grasp is how to further develop the level of abstraction at this point.
Trying to explain better, say I have this, which is often the base example many tutorials use to introduce classes:
class Contact(object):
def __init__(self, name, surname, phone):
self.name = name
self.surname = surname
self.phone = phone
contact1 = Contact('Mark', 'Doe', '123456789')
contact2 = Contact('Sally', 'Preston', '456789123')
So far so good, I can do many other interesting things using contact1.attribute or other methods. No problem here.
What I'm having trouble understanding is the following:
Question 1:
I don't know how many contacts I will have. How do I make a method, say, create_contact(), that makes me create a new contact and store it in a list/dict, if I don't know how many I will have? How do I call it? I can't understand how to make it so that I can create a new instance without hardcoding its name, like "contact1" etc. How do I make the line with "contact1" and "contact2" a dynamic thing?
I tried solving the problem using a list as a class variable. Something like (assuming "contact_list" already exists as a class variable):
Contact.contact_list.append(Contact('Mark', 'Doe','123456789')) # obviously I'd use raw_input instead of 'Mark' etc, but I avoided it here for readability
But I end up with a list of nameless objects, and my brain has a hard time dealing with it. I can access them with list indexes, but I'm not sure I'm on the right track here... any help would be most appreciated.
Question 2: (somewhat related, to better understand the issue)
if in the python CLI I put something like (assuming the previous block defining the class has already been run):
>>> Contact('Bob', 'Stevens', '32165497')
My understanding is that an instance of Contact() does indeed get created, with those attributes... but it has no name. How do I access it? (How do I even know it exists? Is there a way to list all existing instances relative to a certain class?)
I hope I made some sense. Thanks in advance for any kind of help.
There's nothing wrong with having "nameless" instances that get stored in a collection, but I agree that it can be hard to wrap your head around at first. ;) You don't need to know how many contacts you'll be creating, since the Python collection types are all dynamic, so you don't need to specify the size in advance, they'll grow to accomodate the data you feed them.
Here's a demo that uses your Contact class to create a simple phone book in a dictionary of lists. We save each contact both under the first name and the surname, so we can find contacts by either name. The values of the dictionary are lists so we can handle multiple people having the same name.
I added a __repr__ method to Contact to make it easy to display the contents of a Contact instance.
from collections import defaultdict
class Contact(object):
def __init__(self, name, surname, phone):
self.name = name
self.surname = surname
self.phone = phone
def __repr__(self):
return '{name} {surname}: {phone}'.format(**self.__dict__)
phonebook = defaultdict(list)
data = [
('Mark', 'Doe', '123456789'),
('Sally', 'Preston', '456789123'),
('John', 'Doe', '789123456'),
]
for name, surname, phone in data:
contact = Contact(name, surname, phone)
phonebook[name].append(contact)
phonebook[surname].append(contact)
for key, val in phonebook.items():
print(key, val)
output
Mark [Mark Doe: 123456789]
Doe [Mark Doe: 123456789, John Doe: 789123456]
Sally [Sally Preston: 456789123]
Preston [Sally Preston: 456789123]
John [John Doe: 789123456]
Another option is to make phonebook a class attribute of Contact.
Of course, to make this program really useful, we need to be able to save the phonebook to disk, and to be able to load it back in. There are various ways to do that, eg by saving the data to a CSV or JSON file, or to a pickle file. But those are topics for another question. ;)
Q1:
You can create another class that will serve as a database
class Contact(object):
def __init__(self, name, surname, phone):
self.name = name
self.surname = surname
self.phone = phone
class ContactDatabase:
def __init__(self, *args):
self.inner_list = list(args)
def add_contact(self, new_contact):
self.inner_list.append(new_contact)
# Initial contacts
contact1 = Contact('Mark', 'Doe', '123456789')
contact2 = Contact('Sally', 'Preston', '456789123')
# Creating a database
my_database = ContactDatabase(contact1, contact2)
# Adding new contacts later
my_database.add_contact(Contact('Jim', 'Miller', '111223123'))
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)
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).