updated object's attribute in python class, but not getting reflected - python

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.

Related

Hello, when i try to print from my class, i get this error:

Code:
class Passangers():
def __init__(self, passangerNames, nationalities, age, gender):
self.passangerNames = passangerNames
self.nationalities = nationalities
self.age = age
self.gender = gender
def data_base_update(self):
passanger_list = self.passangerNames + " " + self.nationalities + " " + self.age + "" + self.gender
passangers_return = Passangers("fdgfdg", "dfgdfgdf", "dfgfdg", "dfgfdgff",)
print(passangers_return.data_base_update)
Error:
<bound method Passangers.data_base_update of <__main__.Passangers object at 0x00000241A2467C70>>
I get this, but i dont get the print message
Make sure you actually call the method. The way you are doing it, you are just referencing the function object. Instead do, print(passangers_return.data_base_update())
In addition, if you are wanting to get passanger_list back from the function, add return passanger_list at the bottom of the function. Or you can return the string directly by placing the string concatenation you assigned to passanger_list directly after return. To continue, you should use f-strings. They allow you to embed variables and expressions inside strings: return f"{self.x=}" for example.
For your case:
def data_base_update(self):
return f"{self.passangerNames} {self.nationalities} {self.age} {self.gender}"
Pro Tip
You can use __str__ (or __repr__) to display the string representation of your object!
def __str__(self):
return f"{self.passangerNames} {self.nationalities} {self.age} {self.gender}"
Now you can just pass your variable holding the instance to print and get the return value from __str__ (or __repr__) as output!

How to print actual name of variable class type in function?

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?

How to use a list as a class variable so that instance object (arguments) are appended to the list?

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

Accessing instance variables, AttributeError

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

dynamic instances of a class object overwriting each other

I have a simple class that stores simple data. The class is as follows.
class DataFormater:
def __init__(self, N, P, K, price):
self.N = N
self.P = P
self.K = K
self.price = price
The code that calls this class is
from DataFormater import DataFormater
#global variables
ObjectList = [0,1,2,3,4,5,6,7,8,9,10,
11,12,13,14,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,
31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50]
ObjectListCounter = 0
# main
print "enter you N-P-K values, followed by a coma, then the price"
print "example ----> 5 5 5 %50 "
print "return as many values as you want to sort, then enter, 'done!' when done."
while True:
RawData = raw_input()
if RawData == 'done!':
break
else:
ObjectList[ObjectListCounter] = DataFormater
ObjectList[ObjectListCounter].N = int(RawData[0])
# very simple test way of putting first indice in ObjectList[ObjectListCounter].N
ObjectListCounter += 1
print ObjectList[0].N
print ObjectList[1].N
My idea is that ObjectList[0] would create that object '1' that I could call with 1.N
But, when I call these, it seems that I have overwritten the previous instances.
this is what prints...
return as many values as you want to sort, then enter, 'done!' when done.
12
1
done!
1
1
Thanks so much! And I know that my post is messy, I don't exactly know how to make it more "pretty"
So, it looks like you are assigning the actual class (instead of an instance of the class) in your loop. Where you do this:
ObjectList[ObjectListCounter] = DataFormater
I think what you actually want is this
ObjectList[ObjectListCounter] = DataFormater(...insert args here....)
EDIT to address the comments:
Your class init method looks like this:
def __init__(self, N, P, K, price):
That means that to create an instance of your class, it would look like this:
my_formater = DataFormater(1, 2, 3, 4)
You would then be able to access my_formater.N which would have a value of 1.
What you are trying to do instead is access a CLASS level attribute, DataFormater.N. This is generally used in situations where you have a constant variable that does not change between instances of the class. For example:
class DataFormater():
CONSTANT_THING = 'my thing that is always the same for every instance'
You would then be able to access that variable directly from the class, like this:
DataFormater.CONSTANT_THING
I hope that clears things up.

Categories