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.
Related
userInput = input("Enter Name: ")
class person:
def __init__(self, name, age, job):
self.name = name
self.age = age
self.job = job
People = [
person('Josh',23,'Consultant'),
person('Maya',25,'Accountant'),
person('Dan',32,'Social Worker'),
person('Keon',38,'Biomaterials Developer'),
person('Michelle',28,'Surgeon'),
person('Joey',34,'Lawyer')
]
so if userInput = Josh, it would print Josh's name, age, and job
The slow method is to iterate over the list and find a match.
def find_person(people, name):
for person in people:
if person.name == name:
return person
raise ValueError("No matching person found")
This is O(n) in the size of the list, which will cause problems if your list of people is large.
But since you know in advance that you're going to be looking up people by name, you can use the person's name as a dictionary key, effectively indexing by the name rather than an integer value. So rather than creating a list of people, creating a dictionary
people = {
'Josh': person('Josh',23,'Consultant'),
'Maya': person('Maya',25,'Accountant'),
'Dan': person('Dan',32,'Social Worker'),
'Keon': person('Keon',38,'Biomaterials Developer'),
'Michelle': person('Michelle',28,'Surgeon'),
'Joey': person('Joey',34,'Lawyer'),
}
Then "look up a person by name" is as simple as people[name]. Of course, you'll want to hide this complexity behind a class or something and make the client-facing side look like a list, or whatever data structure you want it to look like. This approach also currently assumes that no two people will ever have the same first name, though you can work around that by having a dictionary of lists if there's a possibility of duplicates.
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.
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.
I have created a (somewhat) conversational bot and I want it to be able to create a model of the people whose names it hears. I wish to do this by having it create a new instance of my "person" class, and have that class instance named as that person (as in Bob, Alice, etc.) I've been at this all night, thought I had it because it said I had created the instance, but when trying to reference said instance of the class, it tells me that the name 'Bob' is not defined:
properNouns = [word for word,pos in taggedSent if pos == 'NNP']
if properNouns:
npn = str(properNouns)
print(npn) # ['Bob']
npn = re.sub('[\[\]\,\']', '', npn)
print(npn) # Bob
newPerson = Self(npn,'','','','','','','','','','','','','','') # Put "Bob" as the fName
newPerson.__name__ = npn # Change the instance name to "Bob" (well, try anyway)
print(str(newPerson.__name__) + " was created!") # Bob was created!
print(Bob.fName + " is the first name") # NameError: name 'Bob' is not defined
My base class is as follows:
class Self(): # A starter concept of a Host's concept of Self/Me
def __init__(self, fName, lName, DoB, job, hColor, eColor, sex, homeTown, homeState, whatIHave):
self.fName = fName
self.lName = lName
self.DoB = DoB
self.job = job
self.hColor = hColor
self.eColor = eColor
self.sex = sex
self.homeTown = homeTown
self.homeState = homeState
self.whatIHave = whatIHave
def getFullName(self):
fullName = str(self.fName) + ' ' + str(self.lName)
return fullName.title()
I can successfully manually create an instance and subsequently call up its attributes via dot notation if I hard-code it in advance, but that is not how this needs to work (If in the conversation I say I met with Bob, I need the bot to create a new instance of the Self class named Bob.)
mySelf = Self('Bob','Jones',"Coder",'19730101',black','brown','F','Boston','Massachusetts','a Jeep Wrangler')
And if I subsequently print a Bob.fName, I get Bob....
I can do some fairly decent coding but this is a relatively new area for me, and it may be that I've seen what is the answer but simply don't understand what I'm seeing well enough to know it's what I need, so apologies. I've seen many posts with very similar titles, but they do not seem to have the answer for what I am looking to do here, which seems simple to me: If I create a new "Self" (newSelf = Self), then I want to rename newSelf to "Bob".
Alternatively, and I would think easier, would be to have the new Self created with the proper name to begin with but I can't find a way to take a variable containing the name and make that name = the new Self...
Thanks for your help. I've been at this for over six hours now.
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)