Pull it Together: Cannot access attribute of Class


#1


Pull it Together


The code is passed the correct city in the first run ("Charlotte") however, for some reason, when trying to access the object of the same name of my Cities class, the compiler 'forgets' to use the value of the passed string, instead using the string value 'city'.

To prove the bug, add a 'city' object to the Cities class and assign it a numeric value. The error output will change.

The error output from the below code is:

Oops, try again. plane_ride_cost('Charlotte') raised an error: class Cities has no attribute 'city'

The same is true if we instantiate a new Cities instance, by calling myCities = Cities() and then using myCities.city - except the error now references the instance instead of the class directly.


Exercise should complete and pass all different city inputs (Charlotte, Tampa, Pittsburgh, Los Angeles).


class Cities:
    """A class containing all valid cities"""
    Charlotte = 183
    Tampa = 220
    Pittsburgh = 222
    LosAngeles = 475

def hotel_cost(nights):
    return 140 * nights

def plane_ride_cost(city):    
    if city == 'Los Angeles':
        city = 'LosAngeles'
        
    return Cities.city
    
def rental_car_cost(days):
    total = days * 40;
    
    if days >= 7:
        total -= 50
    elif days >= 3 and days < 7:
        total -= 20
    
    return total

def trip_cost(city, days):
    return rental_car_cost(days) + hotel_cost(days) + plane_ride_cost(city)


#2

Actual authentic genuine organic bug?

I agree, but it resides in your code.

The interpreter doesn't forget, it does as you say.


#3

Hmmmm...

But if it passes a string ('Charlotte'), and I pass this string into the
function var 'city', I should be able to reference this string by simply
using the variable name, yes?

Or do I need some fancy magic to make this line work as intended:

Cities.city

With JavaScript the other way to access an object (key) of a class (object)
would be "Cities[city]" - am I getting closer?


#4

How are you testing that? Because if I load your code and call your plane function:

$ python2 -i <(xsel -b)
>>> plane_ride_cost('Charlotte')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/dev/fd/63", line 15, in plane_ride_cost
    
AttributeError: class Cities has no attribute 'city'

The attribute that you are trying to access is city, you have not defined such an attribute. You have defined a few others however. If you are looking to map cities to costs then you may not want to use a class for that, because what would happen if the "city" is entered as, say, __init__? Or what if there really was a city by that name, how will the class react to having that name overwritten? This problem very much applies to js as well. You need to isolate your data from the internals of your program, they should not mix. What is an appropriate data structure to use here? (In both JS and Python? Because you should not do this for either)


#5

I understand what the code is doing on the offending line.

What I don't understand is that on the line before, if I print 'city', it prints out the passed string.

If accessing the class object should be done differently, I'm struggling to find an example!

Forgive me for my ignorance, I am new here (In Pyland). Data Structures aside (I am not thinking about programming defensively at the moment - I just want to understand why I can't access this classes object in the way I would expect one does).

To be clear - if city == 'Charlotte' then why is Cities.city != Cities.Charlotte?


#6

Your class defines four attributes. None of them are city.
You're accessing city.
You would need to access something like Charlotte instead.

When you write a name, and then a dot, and then a name, then you are accessing a property by that second name. The second name does not refer to variables in local scope, not in Python or in JS.

If you want to solve problems by messing around with the internals of the program, then learn the language first, otherwise you don't have the means to reason about anything you're doing.
If you want to solve basic tasks in a language, then learn a few basic things like variables, int, float, string, array, hashmap, loops, functions, conditions. That's enough to solve a very large amount of problems.

You're saying "I have this problem, and I want to solve it with this terrible solution", the answer is going to be to use the very basic things already in place for this type of problem.


#7

You can do approximately what you describe. There is an associative array that is used to look up attributes in a class. All you need to do is access it. As for how, same as everything else once you've decided what, search for it with a query like "python class attribute dictionary"

But you're formulating your question as "This is an actual bug", where you try to solve a problem that has simpler solutions, and you're saying that you can't do it, instead of considering what "it" is and what you would need to do. Add to that that the idea is coming from something that is possible in js, but isn't a good idea there either.

Your question is very far removed from the answer you are seeking! If you don't know exactly what you're after, then start with that, not "there's a bug"


#8

For others stumped on this: Python requires a special method if trying to access a class attribute via a string (or string type variable). This method is called getattribute or simply getattr for short.

The safest solution, and one that is most readable (a point Python prides itself on), involves a few changes to the original code:

  1. Use an instance of the class instead of the class itself. This doesn't matter so much for this small task, but is good practise and can prevent accidental overwrite errors (classes are mutable)
  2. Use the new 'typed class' sort of declaration. This is what Python 3 and above uses and can add class specific methods based on the type of class, or inherit from parent classes, etc.
  3. Use the correct method, mentioned above, to access the attribute.

class Cities(object): "<-- add type of class to class declaration"
    """A class containing all valid cities"""
    def __init__(self):
	    self.Charlotte = 183
	    self.Tampa = 220
	    self.Pittsburgh = 222
	    self.LosAngeles = 475

def plane_ride_cost(city):    
    validCities = Cities()
    
    if city == 'Los Angeles':
        city = 'LosAngeles'
        
    print dir(validCities)              "<-- debug output to list class methods"
    return getattr(validCities, city)   "<-- correct way to access attribute"
    
print plane_ride_cost('Los Angeles')

#9

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.