Property does not seem to work the right way - python

I can't figure out why the last assert is not working. The error message, "type object 'Student' has no attribute 'name'", confuses me even more, because Student has an attribute called name. Any solutions?
class Student:
def __init__(self, name, imt_name, semester):
self.name = name
self.imt_name = imt_name
self.semester = semester
def get_name(self):
return self.name
def __str__(self):
return ("{} [{}] in Semester {}".format(self.name, self.imt_name, self.semester))
x = property(get_name)
#asserts and test
assert type(Student) is type
student_horst = Student("Horst", "horst99", 2)
assert student_horst.name == "Horst"
assert student_horst.imt_name == "horst99"
assert student_horst.semester == 2
student_horst.semester = 3
assert student_horst.semester == 3
#this one is not working!
assert isinstance(getattr(Student, "name"), property)

name was never a property, x was
class Student:
def __init__(self, name):
self._name = name # this is an attribute
#property
def name(self): # property
return self._name
def get_name(self):
return self._name
x = property(get_name) # x is a property not _name
student = Student('Horst')
print(student._name, student.name, student.x, sep='\n')
assert isinstance(getattr(Student, 'name'), property)
assert isinstance(getattr(Student, 'x'), property)

To check if instance has a given attribute(by name) you need to use the hasattr function, as follows:
assert hasattr(student_horst, "name")

Related

Python updating list from constructor by object.attribute

If a class is created with the attributes: name and list and the name attribute has a default value and the list is appending the name. Is it possible in somehow when I create an object "a" and type
"a.name = 'x' " that this 'x' will appear in the list given that the list is appending in the constructor?
class Person:
list = []
def __init__(self, name="Zed"):
self.name = name
self.list.append(name)
def printList(self):
print(self.list)
a = Person()
a.name = "Yasuo"
a.printList() #outputs Zed but Yasuo is expected.
You can make name a property, and implement a setter that updates the list.
class Person:
list = []
def __init__(self, name="Zed"):
self._name = name
self.list.append(name)
#property
def name(self):
return self._name
#name.setter
def name(self, name):
if self._name in self.list:
# remove the old name
index = self.list.index(self.name)
self.list[index] = name
else:
self.list.append(name)
self._name = name
def printList(self):
print(self.list)
a = Person()
a.name = "Yasuo"
a.printList() # prints ['Yasuo']

Why do I keep getting this Attribute Error?

Every time I run my code it pops up the message saying "'ICU' object has no attribute '_name'. Did you mean: 'name'?" I can not figure out how to fix it. I've tried changing the name of the accessors and mutators but still can't figure out how to solve it. Any suggestions?
Here's my code:
class Patient:
def __init__(self, name, age):
self.name = name
self.age = age
self.weight = 150
#property
def age(self):
return self._age
#age.setter
def age(self, newValue):
if newValue > 0:
self._age = newValue
else:
self._age = 0
#property
def weight(self):
return self._weight
#weight.setter
def weight(self, newValue):
if newValue >=0 and newValue <= 1400:
self._weight = newValue
#IncreaseAge
def increaseAge(self):
self.age = self.age + 1
class In(Patient):
def __init__(self, name, age, stay):
self.name = name
self.age = age
self.stay = stay
#property
def stay(self):
return self._stay
#stay.setter
def stay(self, value):
self._name = value
def __str__(self):
print("IN-" + self._name + self._age + self.weight + self._stay)
class Out(Patient):
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
print("OUT-" + self._name + self._age + self._weight)
class ICU(In):
def __init__(self, name, age):
self.name = name
self.age = age
self.days = 5
class CheckUp(Out):
def __init__(self, name, age):
self.name = name
self.age = age
Here's the rest of the instance:
# Create three patient objects and print them out
p1 = ICU("Ben Dover", 0)
p2 = ICU("Helen Hywater", -15)
p3 = CheckUp("Amanda Lynn", 45)
p4 = ICU("Chester Minit", 12)
p5 = In("Don Keigh", 89, 10)
p6 = Out("Kay Oss ", 45)
print ("\tStatus\tName\t\tAge\tWeight\tStay")
print ("-" * 55)
print ("p1:\t{}".format(p1))
print ("p2:\t{}".format(p2))
print ("p3:\t{}".format(p3))
print ("p4:\t{}".format(p4))
print ("p5:\t{}".format(p5))
print ("p6:\t{}".format(p6))
print ("-" * 55)
# Change their ages and print them out
p1.age = -5
p2.age = 100
for i in range(6):
p3.increaseAge()
p4.age = 0
p5.increaseAge()
p6.age = 42
print ("p1:\t{}".format(p1))
print ("p2:\t{}".format(p2))
print ("p3:\t{}".format(p3))
print ("p4:\t{}".format(p4))
print ("p5:\t{}".format(p5))
print ("p6:\t{}".format(p6))
print ("-" * 55)
# Change other instance variables and print them out
p1.weight = 2000
p1.stay = 3
p2.name = "Justin Thyme"
p2.weight = 220
p2.stay = 0
p3.weight = -50
p4.weight = 1400
p5.weight = 0
p5.stay = 21
p6.weight = 1401
print ("p1:\t{}".format(p1))
print ("p2:\t{}".format(p2))
print ("p3:\t{}".format(p3))
print ("p4:\t{}".format(p4))
print ("p5:\t{}".format(p5))
print ("p6:\t{}".format(p6))
print ("-" * 55)
It's because your variable name is different.
Replace you code from:
#stay.setter
def stay(self, value):
self._name = value
To:
#stay.setter
def stay(self, value):
self.name = value
In Python, constructors - like all other methods - can be overridden. That is once you define __init__ in child classes, the base class method is never called. This is what's causing the error.
You need to explicitly call the base class like like this:
class ICU(In):
def __init__(self, name, age):
self.name = name
self.age = age
self.days = 5
In.__init__(self, name, age, 10) # stay = 10 since it's not an input parameter in the ICU __init__ method.
This needs to be done in every base class. So you'd do something similar in the In class as well.
The problem comme to the fact that "format" is calling "__ str__" on your instances but when "__ str__" get called, some of your instance doesn't have a value for "_name" or "_stay" or "_weight"...see your " __ init __ " method for each instance and execute " __ str __" after you will see the problem. so to handle this case you have the following simple solution
class In(Patient):
def __init__(self, name, age, stay):
self.name = name
self.age = age
self.stay = stay
#property
def stay(self):
return self._stay
#stay.setter
def stay(self, value):
self._name = value
def __str__(self):
x = (
getattr(self, '_name', ''),
getattr(self, '_age', ''),
self.weight or ''
getattr(self, '_stay', ''),
)
return ("IN-%s %s %s %s")%(*x)
class Out(Patient):
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
x = (
getattr(self, '_name', ''),
getattr(self, '_age', ''),
getattr(self, '_stay', ''),
)
return "OUT- %s %s %s"%(*x)
But your classes are not well designed, see below something interesting
class Patient:
def __init__(self, name, age, weight=150):
self._name= name
self._age = age
self._weight = weight
#property
def name(self):
return self._name
#name.setter
def name(self, value):
assert isinstance(value, str)
self._name = value
#property
def age(self):
return self._age
#age.setter
def age(self, value):
assert isinstance(value, int)
self._age = value
#property
def weight(self):
return self._weight
#weight.setter
def weight(self, value):
assert isinstance(value, int)
self._weight = value
def __str__(self):
return f"{self.__class__.__name__.upper()}-{self.name} {self.age} {self.weight}"
class Out(Patient):
pass
class In(Patient):
def __init__(self, name, age, stay, weight=150):
super().__init__(name, age, weight=weight)
self._stay = stay
#property
def stay(self):
return self._stay
#stay.setter
def stay(self, value):
assert isinstance(value, int)
self._stay = value
def __str__(self):
return f"{super().__str__()} {self.stay}"
class ICU(In):
def __init__(self, name, age):
super().__init__(name, age, 5)
class CheckUp(Out):
def __init__(self, name, age):
super().__init__(name, age)
Also note that "increaseAge" method is not defined on your instances

How Can a function act like a descriptor?

def typed_property(name, expected_type):
storage_name = '_' + name
#property
def prop(self):
return getattr(self, storage_name)
#prop.setter
def prop(self, value):
if not isinstance(value, expected_type):
raise TypeError('{} must be a {}'.format(name, expected_type))
setattr(self, storage_name, value)
return prop
class Person:
name = typed_property('name', str)
age = typed_property('age', int)
def __init__(self, name, age):
self.name = name
self.age = age
Function typed_property() acts like a descriptor. Why prop() is called when executing this code line (name = typed_property('name', str))?
I don't know what you mean by "descriptor". typed_property allows a property to call a function for additional processing. prop() is not called when executing the line you mentioned. It is called when executing self.name = name. The #prop.setter makes it so the object can respond to property calls like that.
When you call typed_property to set the value of the class properties name and age, you are really defining those to be methods to use to access the instance values self.name and self.age. This is the same as below omitting age for simplicity:
class Person:
def __init__(self, name):
self.name = name
#property
def name(self):
print("=== ACESSING")
return self.name
#name.setter
def name(self, name):
print("=== MUTATING")
self.name = name
This marks the name(self) method as the accessor for self.name, and name(self, val) as the mutator. The mutator is called whenever you try to change (mutate) the value of its assigned property, in this case self.name. This includes when you are calling it in the __init__ method. However, using the class as defined above will result in an infinite recursion because I am calling the mutator from inside the mutator. So "=== MUTATING" will be printed ending in a recursion error. So a small adjustment is needed:
class Person:
def __init__(self, name):
self._name = name
#property
def name(self):
print("=== ACCESSING")
return self._name
#name.setter
def name(self, val):
print("=== MUTATING")
self._name = val
Now that underlying property is name _name rather than name the mutator will set the value of _name rather than setting it for name and recur into itself infinitely. For example, using the class as defined above:
>>> p = Person("joshmeranda")
>>> p.name
=== ACCESSING
"joshmeranda"

Python class doesn't see second object in list(attribute)

I want to code movie classification class, which can find (based on some criterias), add and print movies.
Here is my code:
class Movie:
def __init__(self,name,director,year,location):
self.name = name
self.director = director
self.year = year
self.location = location
self.information = {'name':self.name,'director':self.director,'year':self.year,'location':self.location}
def get_name(self):
return self.name
def get_director(self):
return self.director
def get_year(self):
return self.year
def get_location(self):
return self.location
def get_information(self):
return self.information
def __str__(self):
return f"Name = {self.name},director = {self.director},year = {self.year},location = {self.location}"
class Classification:
def __init__(self):
self.movie_list = []
def length(self):
return len(self.movie_list)
def __getitem__(self,key):
if isinstance(self.movie_list,slice):
return self.movie_list[key]
def add_movie(self,movie):
self.movie_list.append(movie)
def print_movie(self):
for movie in self.movie_list:
print(movie)
def find_movie(self,**kwargs):
check_list = []
for movie in self.movie_list:
for name,value in kwargs.items():
if movie.get_information()[name] == value:
check_list.append(True)
else:
check_list.append(False)
if all(item == True for item in check_list):
print(movie)
check_list.clear()
Here i have a class Movie and Classification; Classification has only 1 attribute, which is a list of movies. But i have two problems:
a = Movie('Matrix','Dan Yefimov','1999','New York')
b = Movie('Legend','Mak Markus','2005','Kiev')
clasif = Classification()
clasif.add_movie(a)
clasif.add_movie(b)
clasif.find_movie(location = 'New York')
find_movie() works for the first movie (a in our case) in the list. But for the second one it prints nothing, even when I enter correct parameters for a search.
Slicing doesn't work. There is no error message, it just prints nothing.
Can you help me with my problems?
P.S I would like also to hear some general advices about improving of my code.
By removing the unnecessary methods, we can shorten the code considerably. We can also just pass __getitem__ through to the list. I would also use __len__ over defining a length method.
class Movie:
def __init__(self,name,director,year,location):
self.name = name
self.director = director
self.year = year
self.location = location
def __str__(self):
return f"Name = {self.name},director = {self.director},year = {self.year},location = {self.location}"
class Classification:
def __init__(self):
self.movie_list = []
def __len__(self): # __len__ lets you do len(classif)
return len(self.movie_list)
def __getitem__(self,key):
return self.movie_list[key]
def add_movie(self,movie):
self.movie_list.append(movie)
def print_movie(self):
for movie in self.movie_list:
print(movie)
def find_movie(self,**kwargs):
for movie in self.movie_list:
if all(hasattr(movie, k) and getattr(movie, k) == v for k, v in kwargs.items()):
print(movie)
Here is a version of your code will a little less code:
class Movie:
ATTRIBUTES = ('name', 'director', 'year', 'location')
def __init__(self, name, director, year, location):
self.name = name
self.director = director
self.year = year
self.location = location
def __str__(self):
return ', '.join(
'{} = {}'.format(attr_name, getattr(self, attr_name))
for attr_name in self.ATTRIBUTES)
class Classification:
def __init__(self):
self.movie_list = []
def __len__(self):
return len(self.movie_list)
def __getitem__(self, key):
return self.movie_list[key]
def add_movie(self, movie):
self.movie_list.append(movie)
def print_movies(self):
for movie in self.movie_list:
print(movie)
def find_movies(self, **kwargs):
for movie in self.movie_list:
do_print = True
for attr_name, attr_value in kwargs.items():
if attr_name in Movie.ATTRIBUTES:
if getattr(movie, attr_name) != attr_value:
do_print = False
if do_print:
print(movie)
I added a class attribute called ATTRIBUTES to Movie; this is used in Movie.__str__() and also in Classification.find_movies(). It is a suggestion to avoid repeating the attributes a lot in the code.
In the method Classification.find_movies() I check that it is a valid attribute before I compare it to the movie instance. Invalid parameters are ignored, but you could change the code so that invalid parameters automatically cause the movies to not be printed (all will be excluded).

Python property does not set

Here is the code:
def Property(func):
return property(**func())
class A:
def __init__(self, name):
self._name = name
#Property
def name():
doc = 'A''s name'
def fget(self):
return self._name
def fset(self, val):
self._name = val
fdel = None
print locals()
return locals()
a = A('John')
print a.name
print a._name
a.name = 'Bob'
print a.name
print a._name
Above produces the following output:
{'doc': 'As name', 'fset': <function fset at 0x10b68e578>, 'fdel': None, 'fget': <function fget at 0x10b68ec08>}
John
John
Bob
John
The code is taken from here.
Question: what's wrong? It should be something simple but I can't find it.
Note: I need property for complex getting/setting, not simply hiding the attribute.
Thanks in advance.
The documentation for property() states:
Return a property attribute for new-style classes (classes that derive from object).
Your class is not a new-style class (you didn't inherit from object). Change the class declaration to:
class A(object):
...
and it should work as intended.
(Posted above) Use this format:
http://docs.python.org/library/functions.html#property
class C(object):
def __init__(self):
self._name = "nameless"
#property
def name(self):
"""I'm the 'name' property."""
return self._name
#name.setter
def name(self, value):
self._name = value
#name.deleter
def name(self):
del self._name

Categories