strange result in using python's property
Question: I spent some time to learn Python's property magic. But when I am writing a
simple case with a small mistake, I get a strange result. Here is my code:

    
    
    class PropertyShow(object):
        def __init__(self):
            self.__num = 90 
    
        def setNum(self,value):
            self.__num = value
    
        def getNum(self):
            return self.__num
    
        def delNum(self):
            del self.__num
    
        #num = property(getNum,setNum,delNum)
        # I made a mistake here!
        __num = property(getNum,setNum,delNum)
    
    
    class PropertyTwo(object):
        def __init__(self):
            self.__num = None
    
        @property
        def num(self):
            """OK, use a decorator, you can do something here!"""
            return self.__num
    
        @num.setter
        def num(self,value):
            self.__num = value
    
        @num.deleter
        def num(self):
            del self.__num
    
    one = PropertyShow()
    print one.num 
    two = PropertyTwo()
    print two.num
    

In the key line, "num = property(getNum,setNum,delNum)". I changed this line
to this careless, now like this "__num = property(getNum,setNum,delNum)".

The result of this code:

    
    
    File "property.py", line 6, in setNum
        self.__num = value
    RuntimeError: maximum recursion depth exceeded
    

  1. Why do I get this unexpected result when replacing `num` to `__num`?
  2. How to understand the magic of `property()` and why should one use private variables?*


Answer: The line `self.__num = value` results in the property setter being invoked.

The property setter tries to set `self.__num`, which results in the property
setter being invoked. The property setter tries to set `self.__num`, which
results in the property setter being invoked. The property setter tries to set
`self.__num`, which results in the property setter being invoked. The property
setter tries to set `self.__num`, which results in the property setter being
invoked. The property setter tries to set `self.__num`, which results in the
property setter being invoked. The property setter tries to set `self.__num`,
which results in the property setter being invoked. The property setter tries
to set `self.__num`, which results in the property setter being invoked. The
property setter tries to set `self.__num`, which results in the property
setter being invoked.

Infinite recursion detected.

When your property is _not_ named `__num`, this obviously does not happen.

To be explicit: Python sees the line `self.__num = value` as a `STORE_ATTR`
opcode:

    
    
    >>> import dis
    >>> def setNum(self, value):
    ...     self.__num = value
    ... 
    >>> dis.dis(setNum)
      2           0 LOAD_FAST                1 (value)
                  3 LOAD_FAST                0 (self)
                  6 STORE_ATTR               0 (__num)
                  9 LOAD_CONST               0 (None)
                 12 RETURN_VALUE        
    

The implementation for `STORE_ATTR` will first look for a data descriptor on
the class, and finds your `__num` `property` object. The call is then
effectively translated to:

    
    
    PropertyShow.__num.__set__(self, value)
    

The `property` object looks up the configured setter function, which is
`PropertyShow.setNum` and calls it as `PropertyShow.setNum(self, value)`. This
in turn calls `self.__num = value` again and recursion takes over from there.

