Python Class/Objects __str__ & other problems [duplicate] - python

This question already has answers here:
__str__ returned non-string (type tuple)
(3 answers)
Closed 4 years ago.
I'm still very new to Python, and am struggling with what should be a simple assignment. I have to write code for an 'Employee' class, save the module, import the module into another .py file and then store and display 3 objects of that class. I keep getting a
"TypeError: __str__ returned non-string (type tuple)"
no matter how I rework the code, and it's driving me nuts. Anything I've done wrong, please, explain to me how/why it's wrong, learning this is incredibly important to me!
The following is the code for the Employee class:
class Employee:
def __init__(self,name,id,dept,title):
self.__name = name
self.__id = id
self.__dept = dept
self.__title = title
def set_name(self,name):
self.__name = name
def set_id(self,id):
self.__id = id
def set_dept(self,dept):
self.__dept = dept
def set_title(self,title):
self.__title = title
def get_name(self):
return self.__name
def get_id(self):
return self.__id
def get_dept(self):
return self.__dept
def get_title(self):
return self.__title
def __str__(self):
return 'Name: ',self.__name,'\n','ID Number: ',str(self.__id),'\n','Department: ',self.__dept,'\n','Job Title: ',self.__title
I'm not sure I need the set and/or get name methods, because the three objects I have to store and display are pre-determined (no user input or anything). The preceding code is saved in a file named emp.py. The following code is where I think the problem is, but being a novice I don't know for sure.
import emp
def main():
name = 'Susan Meyers'
id = '47899'
dept = 'Accounting'
title = 'Vice President'
employee1 = emp.Employee(name,id,dept,title)
name = 'Mark Jones'
id = '39119'
dept = 'IT'
title = 'Programmer'
employee2 = emp.Employee(name,id,dept,title)
name = 'Joy Rogers'
id = '81774'
dept = 'Manufacturing'
title = 'Engineer'
employee3 = emp.Employee(name,id,dept,title)
print('Employee 1:')
print(employee1)
print('Employee 2:')
print(employee2)
print('Employee 3: ')
print(employee3)
main()
I've tried this by creating an object (i.e. susan = emp.Employee['Susan',id number,'dept','title'] with the appropriate information where id, dept, title are, but still get the tuple error. What am I doing wrong? I considered storing the information in a list or dictionary, but figured I should stick to the bare-bones basics. I feel so stupid, I've been at this all day! For any and all help, thanks in advance.
EDIT: Fixed the indention errors (weren't present in my code in pycharm, but copying and pasting them here w/o proper proofreading...)
FURTHER EDIT:
When run, I need it to say:
Employee 1:
Name: Susan Meyers
ID Number: 47899
Department: Accounting
Title: Vice President
Employee 2:
Name: Mark Jones
ID Number: 39119
Department: IT
Title: Programmer
Employee 3:
Name: Joy Rogers
ID Number: 81774
Department: Manufacturing
Title: Engineer
**And that's the end of the program, like I said, should be really basic stuff, if this were a list or something, I could knock it out np... But each employee has to be stored as an object of the Employee class. We just covered an incredibly long chapter on Classes and Objects (while I was sick with the flu) so my recall/methods may not be the best.

The error's with the __str__ method itself
def __str__(self):
return 'Name: ',self.__name,'\n','ID Number: ',str(self.__id),'\n','Department: ',self.__dept,'\n','Job Title: ',self.__title
"TypeError: __str__ returned non-string (type tuple)"
This error notifies you that
'Name: ',self.__name,'\n','ID Number: ',str(self.__id),'\n','Department: ',self.__dept,'\n','Job Title: ',self.__title
is a tuple. (This is implicitly constructed from the comma-delimited notation.) However, Python is expecting a str as the return type. Change your return statement so that it returns a string. You can use
return ''.join(['Name: ',self.__name,'\n','ID Number: ',str(self.__id),'\n','Department: ',self.__dept,'\n','Job Title: ',self.__title])
or
return 'Name: {}\nID Number: {}\nDepartment: {}\nJob Title: {}'.format(self.__name, self.__id, self.__dept, self.__title)
or anything as long as it returns a string.
Edit: Clarification on Provided Solutions
The first solution uses the .join() method, which follows this format
<str_to_connect>.join(<iterable_of_str>)
The square brackets used ['Name: ',self.__name, ... self.__title] will pack all your various string arguments into a list. Passing this list into .join() connects it all together into a single str.
The second solution uses the .format() method which follows this format
<str_to_format>.format(<args>...)
You can pass complex formatting into the .format() function, but they generally make use of a {} placeholder, which are then filled with input from the arguments passed.
The essential thing is that both these will return str types.
Further reading: str.join(), PyFormat.
Note: C. Kim's solution in the comments, using %, is also equally valid.

Related

[Python]: How to update python property in a good way

I'm new to Python and I'm working on a crawler project.
I have a case want to ask you about good way to handle.
For example.
class Student:
def __init__(
self,
user_id: str,
name: str = None,
age: int = None,
gender: str = None
):
self.name = name
self.age = age
self.gender = gender
user_id = "test_user_id"
# after crawling data by selenium/scrapy
# we have 2 types to build/update class property
# STYLE 1:
student = Student(user_id)
student.name = "AAAA"
student.age = "18"
student.gender = "male"
# STYLE 2:
name = "AAAA"
age = "18"
gender = "male"
student = Student(
user_id=user_id,
name=name,
age=age,
gender=gender)
About the #STYLE 1, I'm not really it's a good way or not. But about #STYLE 2 I think it's gonna have some problem because we have to define a lot of variables (hard to debug), and we have to guarantee the variables have to be initialized before create class instance.
That's my question, please give me your guys idea about this or which way do you guy prefer.
I'm afraid this question will be closed soon as it requires opinion based answers, and is probably out of scope here. Nevertheless it raises an interesting point, so I will give my two cents.
If you are talking about properties that you will set at once, on creation or immediately after, I would definitely go with the second approach. As Punit said in a comment, you can (and usually will) directly pass the values, without creating intermediate variables unless they are already there. And if some of the properties are really necessary to work with the instance, I would avoid specifying a default value, thus making them required.
This way the instance creation is IMO both more readable and more reliable. And if you have really many arguments, you can require that most or all are passed as kwargs, which will further improve readability.
Then you may sometimes have other properties which are not needed, or may be even unknown, at creation time - and of course those will be set later, with the first style.
The best to go about this is to have all this code compressed into a smaller block. This helps make the code look more concise. Also, you forgot to set user_id in STYLE #1.
class Student:
def __init__(self, id: int, name: str, age: int, gender: str):
self.id = id
self.name = name
self.age = age
self.gender = gender
data = [] #scraped data from target site
# assuming a user instance looks like this: { name, age, id, gender }
# using list comprehensions to make it look cool
# you can replace the arguments in `Student()` with whatever fits the
# response from target site
users = [Student(i.id, i.name, i.age, i.gender) for i in data]
Hope this helps!
I would distinguish 2 cases here: Creation of the instance and update.
In the first case you can directly assign the values to the constructor of the class.
classStudent:
def __init__(self, user_id: str, name: str = None, age: int = None, gender: str = None):
self.user_id = user_id # You forgot this attribute!
self.name = name
self.age = age
self.gender = gender
student = Student(
# All the values you have available like you do in STYLE 2
)
In the case of updating it would be easier without using the variables and assigning them directly. Hard to debug, as you said.
student.name = "YYYY"
student.age = "18"
student.gender = "male"
There is nothing wrong in doing this in Python as you don't have to make getters and setters like other languages.
Even so, it would be recommended that you add conditionals to check if the values are valid before making the modification.

How to create a list from a .txt file and then convert the list values into objects

I am a beginner in Python having trouble with a school project. I'm trying to create a library inventory management system based around a file called books.txt
First of all, here is the raw data of books.txt
Mastering Windows Server 2019 - Second Edition;Krause,Jordan;978-1789804539;005.4476/KRA;5;3;
Windows Server 2019 & PowerShell All In One For Dummies;Perrot,Sara;978-1119560715;005.4476/PER;10;2;
Windows Server Automation with PowerShell Cookbook - Fouth Edition;Lee,Thomas;978-1800568457;005.4476/LEE;3;1;
Python Cookbook: Recipes for Mastering Python 3;Beazley,David;978-1449340377;005.133/BEA;10;8;
Automate the Boring Stuff With Python;Sweigart,Al;978-1593275990;005.133/SWE;10;10;
Head First Python - 2nd Edition;Barry,Paul;978-1491919538;005.133/BAR;4;2;
Python Crash Course - 2nd Edition;Matthes,Eric;978-1593279288;005.133/MAT;12;8;
Python for Dummies;Maruch,Stef;978-0471778646;005.133/MAR;5;0;
Beginning Programming with Python for Dummies;Mueller,John Paul;978-1119457893;005.133/MUE;7;5;
Beginning COBOL for Programmers;Coughlan,Michael;978-1430262534;005.133/COU;1;0;
So what I'm trying to do here is store a list of these book objects in a variable. The aim of the program is to modify the list of book objects, rather than modifying the .txt file directly. After that's all done the user can save the changes and THEN overwrite the .txt file. Anyway, I've tried many different things and right now I feel like this function has got me closest in terms of reading and splitting the lines from the file to create a list.
#Apparently 'with open' stops you from having to close the file.
#Copy of display() for the purposes of playing around.
def inventory3():
print("\nName \t \t Author \t \t ISBN \t \t Call Number \t \t Stock \t \t Loaned")
with open("books.txt", "r") as inventoryfile:
for line in inventoryfile:
strip_lines=line.strip()
inventory = strip_lines.split(";")
print(inventory)
This displays all the lines in the books.txt file correctly (I don't want the square brackets to display, but that's a problem for later) and I know from testing (stuff like test = inventory[-3:]) that it functions correctly as a list. Now the aim is to "index the stored list" to create the book objects, and apparently each book object I create should be stored in a separate list. This was the example I was provided.
books.append(Book(line[0],line[1],line[2],line[3],line[4],line[5]))
And I previously created a book class, like so
class Book:
def __init__(self, title, author, isbn, callnumber, stock, loaned):
self.title = title
self.author = author
self.isbn = isbn
self.callnumber = callnumber
self.stock = stock
self.loaned = loaned
def getTitle(self):
return self.title
def getAuthor(self):
return self.author
def getISBN(self):
return self.isbn
def getCallNumber(self):
return self.callnumber
def getStock(self):
return self.stock
def getLoaned(self):
return self.loaned
I'm a bit confused on how I'm meant to link these two together. I'm not seeing the progression from getting the contents of the txt file to display to suddenly converting them all into objects (that can then be individually deleted, new books added, etc). I've spent days Googling and YouTubing but found nothing, so I'm here for help. Thank you very much.
You are almost there! inventory is a list (hence the square brackets).
To create a Book object just update the example you were given to:
books.append(Book(inventory[0],inventory[1],inventory[2],inventory[3],inventory[4],inventory[5]))
To print without the square brackets update your print statement to:
print(' '.join(inventory))
It seems that you want to use the lists created at inventory3 to instantiate your Book class. In this case, you can try to change your function a bit and add a return, like this:
def inventory3():
inventory = []
print("\nName \t \t Author \t \t ISBN \t \t Call Number \t \t Stock \t \t Loaned")
with open("books.txt", "r") as inventoryfile:
for line in inventoryfile:
strip_lines=line.strip()
inventory_line = strip_lines.split(";")[:-1] # Use-1 to get rid of all empty double quotes ('') in the end of each list
print(inventory_line)
inventory.append(inventory_line)
return inventory
With this, you can do:
inventory = inventory3()
books = []
for book_details in inventory:
books.append(Book(book_details[0],book_details[1], book_details[2],book_details[3],book_details[4],book_details[5]))
print(books)
You'll see your objects created.
And, if you really don't need the empty '' in the and of each list, as i suggested, you can do it with list unpacking, it will be more pythonic. Like this:
inventory = inventory3()
books = []
for book_details in inventory:
books.append(Book(*book_details))
print(books)
*book_details will be exactly the same as book_details[0],book_details[1], book_details[2],book_details[3],book_details[4],book_details[5].
EDIT
As said in comments, I'm adding an example using dataclasses.
from dataclasses import dataclass
#dataclass
class Book:
title:str
author:str
isbn:str
callnumber:str
stock:str
loaned:str
# Your Functions
...
If you use a dataclass and print your objects you get something that can help you:
Book(title='Mastering Windows Server 2019 - Second Edition', author='Krause,Jordan', isbn='978-1789804539', callnumber='005.4476/KRA', stock='5', loaned='3')
Instead of:
<__main__.Book object at 0x000001F1D23BFFA0>
If you want something defined by yourself, you can implement the repr method of your book class:
class Book:
def __init__(self, title, author, isbn, callnumber, stock, loaned):
self.title = title
self.author = author
self.isbn = isbn
self.callnumber = callnumber
self.stock = stock
self.loaned = loaned
def __repr__(self):
return self.title + '/' + self.author
# Your Functions
...
Emmacb's answer above answers your question.
Just to add, you can make use of the #dataclass decorator when creating your Book class. This will reduce the code length, and improve readibility. It is also not neccesary to define the Book.getXXX methods as these could be accessed directly by the attribute names
from dataclasses import dataclass
#dataclass
class Book:
title: str
author: str
isbn: str
callnumber: str
stock : str
loaned : str
book_one = Book('Title', 'Author','ISBN', 'Call Number', 'Stock', 'loaned')
book_one_title = book_one.title
print(book_one_title)
This requires you to specify the type of data, which in this example i assumed where all strings.

Classes, self, input variables in self and self default values

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.

Python Object Oriented Programming

I have to write a program to demonstrate a customer using their credit card to check out, I have spent a few hours trying to figure out how to do it and have provided my code below.
I have to make a class, then use it in a main function.
This is what I have so far:
class Customer:
def __init__(self, customer_name, credit_card_num, credit_security_code, debit_card_num, debit_pin):
self.customer_name = name
self.credit_card_num = credit_num
self.credit_security_code = credit_code
self.debit_card_num = debit_num
self.debit_pin = debit_pin
def inputCardInfo(self):
self.customer_name = str(input("Enter your name: "))
self.credit_card_num = str(input("Enter credit card Number: "))
self.credit_security_code = str(input("Enter 3-digit security code: "))
self.debit_card_num = str(input("Enter debit card number: "))
self.debit_pin = str(input("Enter 4-digit PIN: "))
then the main function:
from customer import Customer
def main():
print("Welcome to Wake-Mart. Please register.")
customer_name = input("enter name: ")
customer1 = Customer(customer_name)
print("Registration completed")
main()
I don't know the correct way to call the class methods. I feel if I can figure out how to make one of these work I can figure out the rest.
If you want to understand behaviors and properties more deeply I would recommend making a separate behavior for each value. (get_credit_num, get_debit_num, etc.)
Then, in your main, just call each function individually to get each value.
And to clarify, "class functions", or behaviors, are just things an object can do. You call them the same way you would any function, with the only difference being you put the name of the instance you are calling this behavior for before the function to replace "self". So if you were calling "InputCardInfo" for the object customer1, you would do it like so:
customer1.InputCardInfo(other parameters)
Your code as-is will not work because you are not passing all required parameters when initializing your class.
customer1 = Customer(customer_name)
All of the additional parameters besides self included in your def __init__(self, var1, var2, var3): needs to be passed to the class instance when initializing. There are also variable naming issues with your code but I hope my example below clarifies things for you.
A quick note first to help you better understand: self.customer_name = name does not make sense in your code because there is no parameter named name included in the __init__() method. You must associate an instance variable (self.whatever) to a known variable name passed in through the __init__(self, external_var) method so that self.whatever = external_var. Then, and only then, can you use class methods to call self.whatever and expect to receive the data you passed from external_var. Also, additional parameters you include after self in __init__(self, ..., ...) MUST be passed as variables when creating a class instance.
class Customer:
def __init__(self, customer_name, credit_card_num, credit_security_code, debit_card_num, debit_pin):
self.customer_name = customer_name
self.credit_card_num = credit_card_num
self.credit_security_code = credit_security_code
self.debit_card_num = debit_card_num
self.debit_pin = debit_pin
name = 'Mike'
cc_num = '0000 0000 0000 0000'
code = '111'
debit_num = '1111 1111 1111 1111'
pin = '1234'
new_customer = Customer(name, cc_num, code, debit_num, pin)

Searching a list to see if there is a match python

Basically this is within a class which appends objects of another class to list self. There are 200 objects in list self. So basically if I call self[1] I will get ['John',['Alex', 'Rob']. Basically 'john' refers to self.firstname and the other names refer to there group members. For example the below will print the firstnames and groupmembers of each object for all 200 objects
for line in self:
print line.firstname
for line in self:
print line.groupmembers
Now I have to create something that goes through all the names and checks the names. So basically if John has Alex and Rob as members then there has to be another object with a first name Alex and another object with a firstname Rob. So say there is no object with firstname Alex I want to print 'mismatch'. This is what I have so far but its not doing what its intended to do.
def name(self):
firstnames = []
for item in self:
firstnames.append(item.firstname)
for item1 in self:
for i in item1.groupmembers:
if i not in hello:
print 'mismatch'
Okay so first off, line and self are bad variable names.
self should only be used within a class to be used as a way to call or use its own variables.
Secondly, you say each value in this self list contains values like ['John',['Alex', 'Rob'], but then you go on to use it like a class object... and frankly that don't do make none sense.
So to remedy this, I'm going to assume its done with class objects. I would also rename self to something like school, and instead of calling an element of self; line, which yields no information to the reader.. call it a student!
I'm going to assume your class would start looking like this:
class Student:
# having an empty default value makes it easy to see what types variables should be!
firstname = ""
groupmembers = []
def __init__(self,firstname,groupmembers ):
self.firstname = firstname
self.groupmembers = groupmembers
Then if you have a list of people you can loop through them like so..
>>>school = [Student("foo", ["bar", "that guy"]),
Student("bar", ["foo", "that guy"])]
>>>for student in school:
print student.firstname
print student.groupmembers
foo
["bar", "that guy"]
bar
["foo", "that guy"]
Then to check it a students group members are in school you can add a function to the Student class
class Student:
# having an empty default value makes it easy to see what types variables should be!
firstname = ""
groupmembers = []
def __init__(self,firstname,groupmembers ):
self.firstname = firstname
self.groupmembers = groupmembers
def group_present(self, school):
# This is how you would get all the names of kids in school without list comprehension
attendance = []
for student in school:
attendance.append(student.firstname)
# this is with list comprehension
attendance = [ student.firstname for student in school]
#compare group members with attendance
#note that I write student_name, not student
## helps point out that it is a string not a class
for student_name in self.groupmembers:
if not student_name in attendance:
print "Group member '{}' is missing :o!! GASP!".format(student_name)
In idle:
>>> school[0].group_present(school)
Group member 'that guy' is missing :o!! GASP!
Hope that helps!
I am not sure if i understand exactly but maybe you can use contains
self[1].__contains__('Alex')
this should return true in case of existence or false otherwise.

Categories

Resources