-1

This class example was taken from here.

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    def get_temperature(self):
        print("Getting value")
        return self._temperature

    def set_temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self._temperature = value

    temperature = property(get_temperature, set_temperature)

The idea here is that when we create an instance of Celsius and set the temperature attribute (e.g. foo = Celsus (-1000) ), we want to make sure that the attribute is not less than -273 BEFORE setting the temperature attribute.

I don't understand how it seems to bypass self.temperature = temperature and go straight to the last line. It seems to me like there are three attributes/properties created here: the Class attribute, temperature; the Instance attribute, temperature; and the set_temperature function which sets the attribute _temperature.

What I DO understand is that the last line (the assignment statement) must run the code property(get_temperature, set_temperature) which runs the functions get_temperature and set_temperature and intern sets the private attribute/property _temperature.

Moreover, if I run: foo = Celsius(100) and then foo.temperature, how is the result of foo.temperature coming from temperature = property(get_temperature, set_temperature) and thus _temperature AND NOT self.temperature = temperature? Why even have self.temperature = temperature if temperature = property(get_temperature, set_temperature) gets ran every time the foo.temperature call is made?

More questions...

Why do we have two attributes with the same name (e.g. temperature) and how does the code know to retrieve the value of _temperature when foo.temperature is called?

Why do we need private attributes/properties an not just temperature?

How does set_temperature(self, value) obtain the attribute for parameter value (e.g. the argument that replaces value)?

In short, please explain this to me like a three year old since I have only been programming a few months. Thank you in advance!

tommyc38
  • 703
  • 1
  • 8
  • 21
  • when `Celcius.temperature` is defined as a `property` it overrides the behaviour of the statement `self.temperature = ...` to instead call the setter function `set_temperature` so there is never an instance variable called `temperature` set. – Tadhg McDonald-Jensen Oct 31 '16 at 00:51
  • How does it know to override the statement `self.temperature = temperature`? Because they both have the same name, temperature? – tommyc38 Oct 31 '16 at 00:55
  • yes, but more specifically because you have defined a descriptor in a class, when you do anything with the same name on an instance the descriptor handles what actually happens. Exactly the same thing with methods, if you run `print(self.get_temperature)` it shows a `bound_method` object instead of the function itself, this is because functions are descriptors also. – Tadhg McDonald-Jensen Oct 31 '16 at 00:58
  • Possible duplicate of [How do Python properties work?](http://stackoverflow.com/questions/6193556/how-do-python-properties-work) – Tadhg McDonald-Jensen Oct 31 '16 at 01:00
  • This is not a duplicate. Mine is using a more real world example to illustrate these concepts. I didn't even know what a descriptor was until you mentioned it. I am a newbie and need more explanation given the answer you referenced. – tommyc38 Oct 31 '16 at 01:22
  • Answering every one of your questions in a single answer means people don't get the credit they deserve for answering things, which is the entire point of providing answers here in the first place. On the other hand, a single person answering all of your questions using multiple answers means none of the answers can really be accepted as the correct one since they're all a part of the full answer. To answer most of your questions, anytime you see `x.temperature`, where `x` is an instance of `Celsius`, you're calling the getter or setter method of the property; nothing is "bypassed". –  Oct 31 '16 at 03:08
  • I'd recommend you take a look at [`__gettem__`](https://docs.python.org/3.6/reference/datamodel.html#object.__getitem__) and [`__setitem__`](https://docs.python.org/3.6/reference/datamodel.html#object.__setitem__), as they are closely related to this but maybe easier to understand Then apply the same concept but with `x.temperature` instead of `x[1]`. – Tadhg McDonald-Jensen Oct 31 '16 at 04:21
  • @ChronoKitsune I can see that x.temperature is calling the getter or setter methods because it prints the statements in those fuctions--which was more or less stated in my post. I have seen people on here ask REALLY vague questions that require going over many concepts to get to the final answer. I was trying to be specific in my confusion AND my understanding--thinking that it might help someone pinpoint my issue. Go ahead with the down vote though. You only care about your SO points/status which upsets me. Hopefully not everyone on here is like you!!! – tommyc38 Oct 31 '16 at 04:23
  • @TadhgMcDonald-Jensen thank you for pointing me to those docs. Unfortunately, I have read them and still am none the wiser. I think my biggest hangup might be making the connection of the __init__ function which specifies the attribute `temperature` and what happens with that code when an instance is created. – tommyc38 Oct 31 '16 at 04:29

1 Answers1

2

When we are first taught about classes/objects/attributes we are often told something like this:

When you look up an attribute like x.foo it first looks to see if 'foo' is an instance variable and returns it, if not it checks if 'foo' is defined in the class of x and returns that, otherwise an AttributeError is raised.

This describes what happens most of the time but does not leave room for descriptors. So if you currently think the above is all there is about attribute lookup property and other descriptors will seem like an exception to these rules.

A descriptor basically defines what to do when looking up / setting an attribute of some instance, property is an implementation that lets you define your own functions to call when getting / setting / deleting an attribute.

When you do temperature = property(get_temperature, set_temperature) you are specifying that when x.temperature is retrieved it should call x.get_temperature() and the return value of that call will be what x.temperature evaluates to.

by specifying set_temperature as the setter of the property it states that when ever x.temperature is assigned to something it should call set_temperature with the value assigned as an argument.

I'd recommend you try stepping through your code in pythontutor, it will show you exactly when get_temerature and set_temperature are called after which statements.

Tadhg McDonald-Jensen
  • 20,699
  • 5
  • 35
  • 59
  • I would also like to reference a great article that helped me understand what what going on [here](http://nbviewer.jupyter.org/urls/gist.github.com/ChrisBeaumont/5758381/raw/descriptor_writeup.ipynb). Thanks! – tommyc38 Nov 01 '16 at 00:38