AttributeError: 'str' object has no attribute 'get_grade' - python

hello was following alone in a tutorial and the code that he has is the exact same that i have here but mine doesn't seem to work. when he ran the code his worked completely fine and i am running into errors. code it be that i need to add the parent to the subject such as subject(person)? or is something just wrong. The number get_average function should just return the number but it is having problems with that. appreciate the help
class Person():
def __init__(self, first, last, grade):
self.first = first
self.last = last
self.grade = grade
def get_grade(self):
return self.grade
class Subject():
def __init__(self, subject, number_students):
self.subject = subject
self.number_students = number_students
self.students = []
def add(self, name):
if len(self.students) < self.number_students:
self.students.append(name)
return True
return False
def average(self):
number = 0
for i in self.students:
number += i.get_grade()
return number
p1 = Person("dustin", "white", 83)
subs = Subject("science", 10)
subs.add(p1.first)
print(subs.students)
print(subs.average())

The error arises because you run subs.add(p1.first), which is a type str. p1.first does not have the method get_grade. What you want to run is: subs.add(p1) (the object which will have get_grade). Also, you can remove the redundant parentheses when defining the classes. You can write class Subject(): as class Subject:.
You can then change you add code to:
def add(self, student):
if len(self.students) < self.number_students:
self.students.append(student.first) # changed here
return True
return False

Related

Python TypeError: must be str, not Atom

Can someone tell me in beginner friendly way why am I not being able to print the Molecule name (here in this case 'NaCl') ?
if I replace return Molecule([self, other]) with return Molecule([self.label, other.label]) my code works and produces the expected output but I want to pass instances and not attributes. here is my code:
class Atom:
def __init__(self, label):
self.label = label
def __add__(self, other):
return Molecule([self, other])
class Molecule:
def __init__(self, atoms):
if type(atoms) is list:
self.atoms = atoms
def __repr__(self):
lol = ''
for i in self.atoms:
lol += i
return lol
sodium = Atom("Na")
chlorine = Atom("Cl")
salt = Molecule([sodium, chlorine])
salt = sodium + chlorine
print(salt)
here is the exercise image:
my problem
Your trace is telling you what line you need to look at.
Line 14 you have this
lol += i
Python is struggling with this cause first lol is a string. We know that cause you assigned it this with lol = ''
but now you're asking Python to append an instance of Atom to the str. However you haven't told Python how it's supposed to append a type of Atom to a str.
So you have two options here.
In your Atom class, override the __repr__ function and then convert i to a string.
In your Molecule class, append to lol with i.label rather than just i
Implement the__repr__ function on your Atom class as well, and call it using str(atom);
class Atom:
def __init__(self, label):
self.label = label
def __add__(self, other):
return Molecule([self, other])
def __repr__(self):
return self.label
class Molecule:
def __init__(self, atoms):
if type(atoms) is list:
self.atoms = atoms
def __repr__(self):
lol = ''
for i in self.atoms:
lol += str(i)
return lol
sodium = Atom("Na")
chlorine = Atom("Cl")
salt = Molecule([sodium, chlorine])
salt = sodium + chlorine
print(salt)

Nested data in attribute is not accessible

I have two classes (Student and Course). I'm trying to write a method for the Course class that will remove a given student from a course. However, there's a problem when I run
self.students.remove(student) in the method. The error tells me that student is not in the students list. Printing the students list I don't actually see the values, but instead I see a reference to it:
print(self.students)
> [<data.Student object at 0x7fc9980334f0>, <data.Student object at 0x7fc998033580>, <data.Student object at 0x7fc9980428b0>, <data.Student object at 0x7fc998042a00>]
However, if I select a specific student at an index then I'm able to see the actual data.
print(self.students[0])
> 2020411:King,Maha
Why is this happening when trying to print the students attribute?
Code if needed:
from copy import deepcopy
class Student:
def __init__(self, sid, last, first):
self.sid = sid
self.last = last
self.first = first
def __str__(self):
return '{}:{},{}'.format(self.sid, self.last, self.first)
def __repr__(self):
return '{}:{},{}'.format(self.sid, self.last, self.first)
class Course:
def __init__(self, crn, students):
self.crn = crn
self.students = deepcopy(students)
def key(self):
return self.crn
def is_empty(self):
return len(self.students) == 0
def get_student(self, student_key):
for student in self.students:
if student.key() == student_key:
return deepcopy(student)
return None
def __contains__(self, student):
for i in self.students:
if student.key() == i.key():
return True
break
return False
def register(self, student):
if student not in self:
self.students.append(deepcopy(student))
return
def drop(self, student):
s = None
if student in self:
s = deepcopy(student)
self.students.remove(student)
return s
student1 = Student(2020411, 'King', 'Maha')
student2 = Student(2019399, 'Hess', 'Alvin')
student3 = Student(2020301, 'Chin', 'Yu')
student4 = Student(2019111, 'Hay', 'Ria')
student_list = [student1, student2, student3]
course1 = Course('CP104', student_list)
removed_student = course1.drop(student2)
The issue with deepcopy() is that it creates an entirely new object that has the same attributes as the original one, yet they are not equal. For list.remove(), this compares the reference to check if the actual object exists. In your case, you are trying to remove an object that is not in the list.
Instead of removing it, if you want to return the student, use list.pop().
def drop(self, student):
for i, s in enumerate(self.students):
if s.sid == student.sid :
return self.students.pop(i)
As a side note, it will be easier to do operations if Course.students is a dictionary such that:
self.students = {
`sid1`: student1,
`sid2`: student2,
# etc
}
EDIT: Alternatively, implement __eq__() in Student so that list.remove() will work.
def __eq__(self, other):
return self.sid == other.sid and self.first == other.first and self.last == other.last

How come I see 'None' in output

Learning python from Udacity. Exercise is mentioned below. I cannot see where output 'None' is coming from. Is there something about classes that I am missing ? Thx in advance
Output is always
0
None
======= CODE BEGIN ==============
"""You can use this class to represent how classy someone
or something is.
"Classy" is interchangable with "fancy".
If you add fancy-looking items, you will increase
your "classiness".
Create a function in "Classy" that takes a string as
input and adds it to the "items" list.
Another method should calculate the "classiness"
value based on the items.
The following items have classiness points associated
with them:
"tophat" = 2
"bowtie" = 4
"monocle" = 5
Everything else has 0 points.
Use the test cases below to guide you!"""
class Classy(object):
def __init__(self):
self.items = []
self.classiness = 0
def getClassiness(self):
print(self.classiness)
def createList(self):
self.items.append(item)
def addItem(self, item):
if item=="tophat":
self.classiness+=2
elif item=="bowtie":
self.classiness+=4
elif item=="monocle":
self.classiness+=5
else:
self.classiness+=0
return self.classiness
# Test cases
me = Classy()
# Should be 0
print(me.getClassiness())
Your method getClassiness() is printing and the caller is also printing.
Maybe you meant to return a value rather than printing?
def getClassiness(self):
return self.classiness
class Classy(object):
def __init__(self):
self.items = []
self.classiness = 0
def getClassiness(self):
return self.classiness
def createList(self):
self.items.append(item)
def addItem(self, item):
if item=="tophat":
self.classiness+=2
elif item=="bowtie":
self.classiness+=4
elif item=="monocle":
self.classiness+=5
else:
self.classiness=0
Test cases
me = Classy()
Should be 0
print(me.getClassiness())

How to fix infinite loop spawning from __str__ method?

File "C:\Users\kevin\Documents\Programs\ParLumen\trait.py", line 76, in __str__
ret_str = super().__str__()
File "C:\Users\kevin\Documents\Programs\ParLumen\trait.py", line 39, in __str__
ret_str += f'{self.name}\n'
RecursionError: maximum recursion depth exceeded
There are many, many more lines of this, however it's just the same two lines repeating, and then the final, different line is, "RecursionError: maximum recursion depth exceeded"
I'm not really sure why this is occurring.
Here is the code causing the issue:
from abc import ABC, abstractmethod
from enum import Enum, unique
from parlumen.game import *
# Base Trait class
class Trait(ABC):
def __init__(self, name, short_desc="No Desc", long_desc=None):
self.name = name
# self.value = value # may restrict this with req. 'avail_values()' func
"""
if short_desc is not "No Desc":
self.short_desc = short_desc
"""
self.short_desc = short_desc
if long_desc is None:
self.long_desc = self.short_desc
else:
self.long_desc = long_desc
#abstractmethod
def __str__(self):
ret_str = ""
ret_str += f'{self.name}\n'
ret_str += f'{self.long_desc}'
return ret_str
# Area of Effect Definition
#unique
class Focus(Enum):
NoFocus = 'None'
Origin = 'Origin'
Reversed = 'Reversed'
class AreaOfEffect(Trait):
def __init__(self, area, focus=None):
super().__init__(self, "Area of Effect")
self.area = area
if focus is None :
self.focus = Focus.NoFocus
else:
self.focus = focus
self.long_desc = f"Gives an attack or spell an area of effect of self.area"
def __str__(self):
ret_str = super().__str__()
ret_str += f"\nArea of Effect: {self.area}, Focus: {self.focus}"
return ret_str
aoe = AreaOfEffect(3, Focus.Origin)
print(aoe)
More specifically, the issue seems to pertain my __str__ method for Trait seems to not like having self.name or self.long_desc -- that is when the issue occurs.
This is sort of a mess in general, but I'm really trying to figure this out before continuing on. Strange that it only happens for Trait and not the subclass AreaOfEffect's __str__
You need to change:
super().__init__(self, "Area of Effect")
To:
super().__init__("Area of Effect")
you are setting name to an object instance, which then calls str recursively when trying to format name.

How to implement a Decorator with non-local equality?

Greetings, currently I am refactoring one of my programs, and I found an interesting problem.
I have Transitions in an automata. Transitions always have a start-state and an end-state. Some Transitions have a label, which encodes a certain Action that must be performed upon traversal. No label means no action. Some transitions have a condition, which must be fulfilled in order to traverse this condition, if there is no condition, the transition is basically an epsilon-transition in an NFA and will be traversed without consuming an input symbol.
I need the following operations:
check if the transition has a label
get this label
add a label to a transition
check if the transition has a condition
get this condition
check for equality
Judging from the first five points, this sounds like a clear decorator, with a base transition and two decorators: Labeled and Condition. However, this approach has a problem: two transitions are considered equal if their start-state and end-state are the same, the labels at both transitions are equal (or not-existing) and both conditions are the same (or not existing). With a decorator, I might have two transitions Labeled("foo", Conditional("bar", Transition("baz", "qux"))) and Conditional("bar", Labeled("foo", Transition("baz", "qux"))) which need a non-local equality, that is, the decorators would need to collect all the data and the Transition must compare this collected data on a set-base:
class Transition(object):
def __init__(self, start, end):
self.start = start
self.end = end
def get_label(self):
return None
def has_label(self):
return False
def collect_decorations(self, decorations):
return decorations
def internal_equality(self, my_decorations, other):
try:
return (self.start == other.start
and self.end == other.end
and my_decorations = other.collect_decorations())
def __eq__(self, other):
return self.internal_equality(self.collect_decorations({}), other)
class Labeled(object):
def __init__(self, label, base):
self.base = base
self.label = label
def has_label(self):
return True
def get_label(self):
return self.label
def collect_decorations(self, decorations):
assert 'label' not in decorations
decorations['label'] = self.label
return self.base.collect_decorations(decorations)
def __getattr__(self, attribute):
return self.base.__getattr(attribute)
Is this a clean approach? Am I missing something?
I am mostly confused, because I can solve this - with longer class names - using cooperative multiple inheritance:
class Transition(object):
def __init__(self, **kwargs):
# init is pythons MI-madness ;-)
super(Transition, self).__init__(**kwargs)
self.start = kwargs['start']
self.end = kwargs['end']
def get_label(self):
return None
def get_condition(self):
return None
def __eq__(self, other):
try:
return self.start == other.start and self.end == other.end
except AttributeError:
return False
class LabeledTransition(Transition):
def __init__(self, **kwargs):
super(LabeledTransition).__init__(**kwargs)
self.label = kwargs['label']
def get_label(self):
return self.label
def __eq__(self):
super_result = super(LabeledTransition, self).__eq__(other)
try:
return super_result and self.label == other.label
except AttributeError:
return False
class ConditionalTransition(Transition):
def __init__(self, **kwargs):
super(ConditionalTransition, self).__init__(**kwargs)
self.condition = kwargs['condition']
def get_condition(self):
return self.condition
def __eq__(self, other):
super_result = super(ConditionalTransition, self).__eq__(other)
try:
return super_result and self.condition = other.condition
except AttributeError:
return False
# ConditionalTransition about the same, with get_condition
class LabeledConditionalTransition(LabeledTransition, ConditionalTransition):
pass
the class LabledConditionalTransition behaves exactly as expected - and having no code in there is appealing and I do not thing MI is confusing at this size.
Of course, the third option would be to just hammer everything into a single transition class with a bunch of in has_label/has_transition.
So... I am confused. Am I missing something? Which implementation looks better? How do you handle similar cases, that is, objects which look like a Decorator could handle them, but then, such a non-local method comes around?
EDIT:
Added the ConditionalTransition-class. Basically, this kinda behaves like the decorator, minus the order created by the order of creating the decorators, the transition checks for start and end being correct, the LabeledTransition-class checks for label being correct and ConditionalTransition checks for condition being correct.
I think its clear that nobody really understands your question. I would suggest putting it in context and making it shorter. As an example, here's one possible implementation of the state pattern in python, please study it to get an idea.
class State(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name
class Automaton(object):
def __init__(self, instance, start):
self._state = start
self.transitions = instance.transitions()
def get_state(self):
return self._state
def set_state(self, target):
transition = self.transitions.get((self.state, target))
if transition:
action, condition = transition
if condition:
if condition():
if action:
action()
self._state = target
else:
self._state = target
else:
self._state = target
state = property(get_state, set_state)
class Door(object):
open = State('open')
closed = State('closed')
def __init__(self, blocked=False):
self.blocked = blocked
def close(self):
print 'closing door'
def do_open(self):
print 'opening door'
def not_blocked(self):
return not self.blocked
def transitions(self):
return {
(self.open, self.closed):(self.close, self.not_blocked),
(self.closed, self.open):(self.do_open, self.not_blocked),
}
if __name__ == '__main__':
door = Door()
automaton = Automaton(door, door.open)
print 'door is', automaton.state
automaton.state = door.closed
print 'door is', automaton.state
automaton.state = door.open
print 'door is', automaton.state
door.blocked = True
automaton.state = door.closed
print 'door is', automaton.state
the output of this programm would be:
door is open
closing door
door is closed
opening door
door is open
door is open
From the code that was posted, the only difference between Transition and Labeled Transition is the return of get_lable() and has_label(). In which case you can compress these two a single class that sets a label attribute to None and
return self.label is not None
in the has_label() function.
Can you post the code for the ConditionalTransition class? I think this would make it clearer.

Categories

Resources