I have a few related questions about instance variables in Python. I will put the first (main) question in a comment inside the code itself and ask the related ones afterwards:
class Employee:
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.email = first + '.' + last + '#email.com'
self.pay = pay
def fullname(self):
return '{} {}'.format(self.first, self.last)
#classmethod
def from_string(cls, emp_str):
first, last, pay = emp_str.split(',')
return cls(first, last, pay)
emp_str_1 = 'John,Doe,70000'
emp_1 = Employee.from_string(emp_str_1)
print(emp_1.fullname(), emp_1.pay, emp_1.email) #<--- This works
print(emp_1.fullname().pay.email) #<--- Why can't I do this??
Also, why is it called a "str object" by the error message:
AttributeError: 'str' object has no attribute 'pay'
Isn't emp_1 an instance of Employee?
Last question, (this may just be a PyCharm issue) PyCharm does not attempt to warn me that this code will break before I try and run it, why?
In Python, concatenation can be done using +
print(emp_1.fullname(), emp_1.pay, emp_1.email) # THIS WILL WORK,
because you are just passing Strings. [ NOT RELATED to this question, just additional info : In case if you integers or boolean to print, then it will fail unless you explicitly typecast it with str())
print(emp_1.fullname().pay.email) # THIS IS NOT WORKING,
because '.' operator is used to access instance methods and variables.
If you use, emp_1.fullname(), it is calling the fullname() method which is returning some string.
But if you use emp_1.fullname().pay - emp_1.fullname() is a call to the Method and fullname() method doesn't have any variables like 'pay'. So the correct way to use is emp_1.pay.
I would suggest to use print like,
print("Name :{}:, Pay :{}: , Mail :{}:".format(emp_1.fullname(), emp_1.pay, emp_1.email)) # BECAUSE format will typecast in case if your pay is INTEGER OR FLOAT
Related
I'm trying to return variable name, but i keep getting this:
<classes.man.man object at (some numbers (as example:0x03BDCA50))>
Below is my code:
from classes.man import man
def competition(guy1, guy2, counter1=0, counter2=0):
.......................
some *ok* manipulations
.......................
if counter1>counter2:
return guy1
bob = man(172, 'green')
bib = man(190, 'brown')
print(competition(bob , bib ))
Epilogue
If anyone want to, explain please what I can write instead of __class__ in example below to get variable name.
def __repr__(self):
return self.__class__.__name__
Anyway, thank you for all of your support
There are different ways to approach your problem.
The simplest I can fathom is if you can change the class man, make it accept an optional name in its __init__ and store it in the instance. This should look like this:
class man:
def __init__(number, color, name="John Doe"):
self.name = name
# rest of your code here
That way in your function you could just do with:
return guy1.name
Additionnally, if you want to go an extra step, you could define a __str__ method in your class man so that when you pass it to str() or print(), it shows the name instead:
# Inside class man
def __str__(self):
return self.name
That way your function could just do:
return guy1
And when you print the return value of your function it actually prints the name.
If you cannot alter class man, here is an extremely convoluted and costly suggestion, that could probably break depending on context:
import inspect
def competition(guy1, guy2, counter1=0, counter2=0):
guy1_name = ""
guy2_name = ""
for name, value in inspect.stack()[-1].frame.f_locals.items():
if value is guy1:
guy1_name = name
elif value is guy2:
guy2_name = name
if counter1 > counter2:
return guy1_name
elif counter2 > counter2:
return guy1_name
else:
return "Noone"
Valentin's answer - the first part of it at least (adding a name attribute to man) - is of course the proper, obvious solution.
Now wrt/ the second part (the inspect.stack hack), it's brittle at best - the "variables names" we're interested in might not necessarily be defined in the first parent frame, and FWIW they could as well just come from a dict etc...
Also, it's definitly not the competition() function's responsability to care about this (don't mix domain layer with presentation layer, thanks), and it's totally useless since the caller code can easily solve this part by itself:
def competition(guy1, guy2, counter1=0, counter2=0):
.......................
some *ok* manipulations
.......................
if counter1>counter2:
return guy1
def main():
bob = man(172, 'green')
bib = man(190, 'brown')
winner = competition(bob, bib)
if winner is bob:
print("bob wins")
elif winner is bib:
print("bib wins")
else:
print("tie!")
Python prints the location of class objects in memory if they are passed to the print() function as default. If you want a prettier output for a class you need to define the __repr__(self) function for that class which should return a string that is printed if an object is passed to print(). Then you can just return guy1
__repr__ is the method that defines the name in your case.
By default it gives you the object type information. If you want to print more apt name then you should override the __repr__ method
Check below code for instance
class class_with_overrided_repr:
def __repr__(self):
return "class_with_overrided_repr"
class class_without_overrided_repr:
pass
x = class_with_overrided_repr()
print x # class_with_overrided_repr
x = class_without_overrided_repr()
print x # <__main__.class_without_overrided_repr instance at 0x7f06002aa368>
Let me know if this what you want?
I would like to simply make a list of kinds of coffe, but get an error stating that the list is not defined. Do I have to use self in the constructor when referencing to a classvariable?
I have tried changing the return statement to return self.coffelist.append(name), but then get another error: 'Function' object has no attribute 'append'.
class coffe:
coffelist = []
def __init__(self,name,origin,price):
self.name = name
self.origin = origin
self.price = price
return (self.coffelist.append(self.name))
def coffelist(self):
print(coffelist)
c1=coffe("blackcoffe","tanz",55)
c2=coffe("fineroasted","ken",60)
This is because you named one of your methods as coffelist.
I think this shows how to do what you want. I also modified your code to follow the PEP 8 - Style Guide for Python Code and corrected some misspelled words.
class Coffee: # Class names should Capitalized.
coffeelist = [] # Class attribute to track instance names.
def __init__(self,name,origin,price):
self.name = name
self.origin = origin
self.price = price
self.coffeelist.append(self.name)
def print_coffeelist(self):
print(self.coffeelist)
c1 = Coffee("blackcoffee", "tanz", 55)
c1.print_coffeelist() # -> ['blackcoffee']
c2 = Coffee("fineroasted", "ken", 60)
c1.print_coffeelist() # -> ['blackcoffee', 'fineroasted']
# Can also access attribute directly through the class:
print(Coffee.coffeelist) # -> ['blackcoffee', 'fineroasted']
yes thanks that's exactly what I wanted!
I wasnt sure.. I thought you could do 2 things simultaneously in the return statement, both return append. I guess allot of times python is very flexible and sometimes not. thanks
consider my simple class
class stud():
def __init__(self,a,b):
self.name=a
self.mark=b
self.message=self.name + ' ' +str(self.mark)
s1=stud('student1',40)
print s1.message --->output=student1 40
s1.name='student2'
print s1.message ----> output =student1 40 , but i expected student2 40
My question here is why when I printed the self.message [after modifying the name attribute of the object], it printed the old value? I know init method is called only once during object creation and name attribute is set to value 'student1' at that time. But, i am changing it next line and again printing of self.message shouldn't list the new value?
Why is this self.message is not updating the modified value?
Trace the execution.
s1=stud('student1',40)
This sets
s1.name to "student1"
s1.mark to 40
s1.message to "student1 40"
Think of the attribute values as boxes. Each of the three boxes have a value in them. Two of them hold strings. One of them holds a number. They will hold those values until they are explicitly assigned to. In particular the attribute message has NO IDEA how it got its value. It has no idea it got its value by concatenating the name attribute and the mark attribute. All it knows is that the value it has is "student1 40".
print s1.message
This outputs student1 40 (no surprise)
s1.name='student2'
You updated one of the three attributes. But you did not update the others.
print s1.message
Since you never explicitly changed s1.message, this still outputs student1 40.
The point is the value of s1.message is just a string. There is no invisible computation that automatically updates it whenever the expression that initialized it gets changed. Other languages work that way perhaps, but Python does not.
If you want that behavior, then do this:
class stud():
def __init__(self,a,b):
self.name=a
self.mark=b
#property
def message():
return self.name + ' ' +str(self.mark)
That is because you initialize the self.message in the init, and when you did s1.name = student2, you only changed the self.name, while the self.message was already initialized.
If you want to get the self.message, you want to update its values somehow.
what will work:
class stud():
def __init__(self,a,b):
self.name=a
self.mark=b
def the_message(self):
self.message = self.name + ' ' + str(self.mark)
return self.message
s1 = stud('student1',40)
print s1.the_message()
s1.name='student2'
print s1.the_message()
output:
student1 40
student2 40
When you call the constructor, it initializes name, mark and message.
If you modify name, you don’t call the constructor, so the message is not updated.
This statement is not called again:
self.message=self.name + ' ' +str(self.mark)
To do that, you need a function or a property to calculate the message each time you need it.
def get_message(self):
return self.name + ' ' + str(self.mark)
s1 = Stud('student1', 40)
print(s1.message)
s1.name = 'student2'
print(s1.get_message())
To use a property, you need to inherit from object (because this functionality is only available with the new style class in Python 2).
You can do that:
class Stud(object):
def __init__(self, a, b):
self.name = a
self.mark = b
#property
def message(self):
return self.name + ' ' + str(self.mark)
s1 = Stud('student1', 40)
print(s1.message)
s1.name = 'student2'
print(s1.message)
Note on code style:
class names should be in CamelCase,
variable and function names should be in snake_case.
Because you have only changed the name attribute not the message so its still printing the same message.
What you need is define your message as an function rather than an attribute. Go ahead and try it.
I am trying to add a method to update my acct_type, but I keep getting an error when trying to print out the new account.
class BankAccount(object):
def __init__(self, acct_holder, acct_type, balance = 0):
self.holder = acct_holder
self.type = acct_type
self.balance = balance
"""
Write a constructor for a bank account below.
The following attributes:
acct_holder will be a Person object
acct_type is a string
balance should be defaulted to 0 if not provided to the constructor, is an int
"""
pass
def changeAccountType(self, newType):
self.type = str(self.newType)
def __str__(self):
return str(self.holder) + self.type + str(self.balance)
account_1 = BankAccount('James','checking',45)
account_1.type.changeAccountType("dookieball_account")
print(account_1)
From your code above, I removed the ".type" from "account_1.type.changeAccountType..." and also removed "self." from "self.newType" in your changeaccount function. newType is not contained in self. Hope this help, it's my first attempt on here!
account_1.type.changeAccountType("dookieball_account")
needs to be
account_1.changeAccountType("dookieball_account")
When you use .type, you are getting the type of account_1, which is a string, and trying to call the changeAccountType function on it. Removing it instead calls the function on account_1.
Also, change
self.type = str(self.newType)
to
self.type = str(newType)
Because the newType is a parameter of that function, it is not yet under self.
pI am working on a bit of code that does nothing important, but one of the things I am trying to make it do is call a function from another class, and the class name is pulled out of a list and put into a variable. Mind you I have literally just learned python over the last 2 weeks, and barely know my way around how to program.
What I believe that this should do is when getattr() is called, it will pass the attribute 'run_question' that is contained in the respective class with the same name as what is in question_type, and then pass it onto 'running_question'. I know there are probably better ways to do what I am attempting, but I want to know why this method doesn't work how I think it should.
#! /usr/bin/python
rom random import randrange
class QuestionRunner(object):
def __init__(self):
##initialize score to zero
self.score = 0
##initialize class with the types of questions
self.questiontypes = ['Addition', 'Subtraction', 'Division', 'Multiplication']
##randomly selects question type from self.questiontypes list
def random_type(self):
type = self.questiontypes[randrange(0, 4)]
return type
##question function runner, runs question function from self
def run_questions(self):
try:
question_type = self.random_type()
running_question = getattr(question_type, 'run_question' )
except AttributeError:
print question_type
print "Attribute error:Attribute not found"
else: running_question()
class Question(object):
pass
class Multiplication(Question):
def run_question(self):
print "*"
class Division(Question):
def run_question(self):
print "/"
class Subtraction(Question):
def run_question(self):
print "-"
class Addition(Question):
def run_question(self):
print "+"
test = QuestionRunner()
test.run_questions()
This outputs:
[david#leonid mathtest] :( $ python mathtest.py
Division
Attribute error:Attribute not found
[david#leonid mathtest] :) $
Which indicates that I am not getting the run_question attribute as I expect.
I should note that when I put the functions into the QuestionRunner class in the following way, everything works as expected. The main reason I am using classes where it really isn't needed it to actually get a good grasp of how to make them do what I want.
#! /usr/bin/python
from random import randrange
class QuestionRunner(object):
def __init__(self):
##initialize score to zero
self.score = 0
##initialize class with the types of questions
self.questiontypes = ['addition', 'subtraction', 'division', 'multiplication']
##randomly selects question type from self.questiontypes list
def random_type(self):
type = self.questiontypes[randrange(0, 4)]
return type
##question function runner, runs question function from self
def run_questions(self):
try:
question_type = self.random_type()
running_question = getattr(self, question_type)
except AttributeError:
exit(1)
else: running_question()
def multiplication(self):
print "*"
def division(self):
print "/"
def addition(self):
print "+"
def subtraction(self):
print "-"
test = QuestionRunner()
test.run_questions()
Any help on why this isn't working would be great, and I appreciate it greatly.
Any help on why this isn't working would be great, and I appreciate it greatly.
Ah, I have found out the missing concept that was causing my logic to be faulty. I assumed that I could pass the name of an object to getattr, when in reality I have to pass the object itself.