Decorator

Chris Angelico rosuav at gmail.com
Thu Feb 9 02:19:07 EST 2017


On Thu, Feb 9, 2017 at 6:03 PM, ast <nomail at com.invalid> wrote:
> class Temperature:
>       def __init__(self):
>        self.value = 0
>
> #  @property
>    def celsius(self):        return self.value
>       celsius = property(celsius)
> #  @celsius.setter      def celsius(self, value):                <--
> overwrites previous celsius
>        self.value = value
>
>    celsius = celsius.setter(celsius)     <-- error here
>

The difference is that the decorator line is evaluated before the
function is defined. Try this:

class Temperature:
    def __init__(self):
        self.value = 0

    #@property
    def celsius(self):
        return self.value
    celsius = property(celsius)

    #@celsius.setter
    _tmp = celsius.setter
    def celsius(self, value):
       self.value = value
    celsius = _tmp(celsius)
    del _tmp

Now it'll work.

(Actually, there are some other very minor subtleties; the name isn't
temporarily bound to the undecorated function prior to the decorator
being called. But in this case, it's simply an order of evaluation.)

In CPython (3.7 on Debian Linux, fwiw), the decorated function is
processed like this:

    @celsius.setter
    #_tmp = celsius.setter
    def celsius(self, value):
       self.value = value
    #celsius = _tmp(celsius)
    #del _tmp


11          28 LOAD_NAME                5 (celsius)
             30 LOAD_ATTR                6 (setter)
             32 LOAD_CONST               5 (<code object celsius at
0x7f8ba4b735d0, file "123123123.py", line 11>)
             34 LOAD_CONST               4
('Temperature.<locals>.Temperature.celsius')
             36 MAKE_FUNCTION            0
             38 CALL_FUNCTION            1
             40 STORE_NAME               5 (celsius)

In other words:
1) Evaluate "celsius.setter" and save that on the stack
2) Fetch up the compiled code for the undecorated function
3) Grab the name of the function (it's a constant)
4) Create a function. Don't save it anywhere yet, just put it on the stack.
5) Call the function from step 1, passing the function from step 4 as
a parameter.
6) Whatever it returns, save that under the name "celsius".

That's very detailed and nitty-gritty, but that's what really happens
when you use "@celsius.setter" in your code.

ChrisA



More information about the Python-list mailing list