1

I am running the following code:

class testClass:
    def __init__(self, left, width):
        self.__left = left
        self.__width = width

    @property
    def left(self):
        return self.__left

    @left.setter
    def left(self, newValue):
        self.__left = newValue

    @property
    def width(self):
        return self.__width

    @width.setter
    def width(self, newValue):
        self.__width = newValue

    def right(self):
        return self.__width + self.__left

    def rightFixed(self):
        return self.width + self.left

test = testClass(10,5)
test.left = 50
print test.right()
print test.rightFixed()

I am getting the values

15
55

Can anyone explain why the first method test.right() is giving the value 15, whereas if I call the test.rightFixed() value it gives me the appropriate value? I have looked in the interpreter, and _testClass__left after the code has run gives me 10, whereas it should give me 50. The @left.setter property doesn't seem to be updating the self.__left, rather it seems to be making it's own copy.

EDIT: I should also note, I am running 2.7.6. As Games Brainiac pointed out, this works fine in python 3+.

ixwt
  • 13
  • 3
  • I'm getting 55, 55. Using python 3 tho. – Games Brainiac Oct 21 '14 at 02:09
  • I forgot to mention I am using 2.7. – ixwt Oct 21 '14 at 02:10
  • 1
    Why are you using double underscores? – dursk Oct 21 '14 at 02:12
  • @mattm Yea, if he does that then it gets added as class attrs. – Games Brainiac Oct 21 '14 at 02:12
  • This is part of a group project. The member who wrote the code that is causing this issue wants to do encapsulation. I told him it's not the way it's normally done, but he comes from a world of Fortran and C++. He is dead set on private variables. – ixwt Oct 21 '14 at 02:14
  • Well if it's a private variable you shouldn't be doing `test.__left` anyway. – dursk Oct 21 '14 at 02:15
  • @ixwt `_var` is set to private mate, not `__var` – Games Brainiac Oct 21 '14 at 02:15
  • 2
    @ixwt: double underscore isn't private, it's superprivate, and pretty unpythonic. You're teammate is in a world of hurt if he is going to try to write Python as if it were C++. – dursk Oct 21 '14 at 02:17
  • Off topic to the original question, but: Just because you're used to another language doesn't mean you should try to force that languages practices on another. There's a reason `from __future__ import braces` acts the way it does. Tell your friend to deal with it. – Parker Oct 21 '14 at 02:23
  • @mattm I told him about the naming convention, and I was using [this SO post](http://stackoverflow.com/questions/1641219/does-python-have-private-variables-in-classes) and [This reference](https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references) document which both say that double underscore is private. – ixwt Oct 21 '14 at 02:24
  • Reject any of his code containing self.__var... Point him to those references or here. – smci Oct 21 '14 at 02:25
  • 2
    Unless you're trying to "avoid name clashes of names with names defined by subclasses", using `__` is nine kinds of silly, and ugly to boot. – DSM Oct 21 '14 at 02:28
  • @ixwt: Double underscore is technically private, it's just not the pythonic way to do private variables. Static languages have public/private keywords, and the double underscore is close to that in Python, but it goes against what Python is about. A single underscore is a naming convention in Python to indicate to the programmer that it's a private variable. – dursk Oct 21 '14 at 02:29
  • @mattm. I just looked it up in PEP8. It seems you're mostly right. Double underscore should only be used in the case that you don't want an inheriting class to mess with values, which doing it this way is still unpythonic in a sense. – ixwt Oct 21 '14 at 02:37

2 Answers2

5

Add (object) to your class. After Python 2.6 a new data model was introduced. See https://docs.python.org/2/reference/datamodel.html#newstyle.

See the comments by DSM for why Python3 and Python2 treat it different.

class testClass(object):
    def __init__(self, left, width):
        self.__left = left
        self.__width = width

    @property
    def left(self):
        return self.__left

    @left.setter
    def left(self, newValue):
        self.__left = newValue

    @property
    def width(self):
        return self.__width

    @width.setter
    def width(self, newValue):
        self.__width = newValue

    def right(self):
        return self.__width + self.__left

    def rightFixed(self):
        return self.width + self.left

>>test = testClass(10,5)
>>test.left = 50
>>print test.right()
55
>>print test.rightFixed()
55
Parker
  • 8,539
  • 10
  • 69
  • 98
  • This seems to fix the problem. Although I am curious as to why. – ixwt Oct 21 '14 at 02:19
  • 1
    This solves the problem because you need to be a new-style class to have descriptor support, and properties are descriptors. "(Note that descriptors are only invoked for new style objects or classes (a class is new style if it inherits from object or type)." See [here](https://docs.python.org/2/howto/descriptor.html) for more than you care to know. – DSM Oct 21 '14 at 02:22
  • In Python 3, all classes are 'new-style' classes. – Joel Cornett Oct 21 '14 at 02:26
  • 1
    @DSM Not really. `property` is lazy by default in py2, its not in py3. – Games Brainiac Oct 21 '14 at 02:27
  • 1
    @GamesBrainiac: ? `property` doesn't apply to old-style classes. – DSM Oct 21 '14 at 02:30
  • @DSM It does. Even in the docs, properties are used, without inheriting from object. – Games Brainiac Oct 21 '14 at 02:31
  • @GamesBrainiac: take an example from the docs, say the last one in [this section](https://docs.python.org/2/library/functions.html#property) of the docs, and add `print "setter was called"` to the setter method. Instantiate it, type `a.x = 2`, and watch "setter was called" be printed. Then remove `object`, and try it again. – DSM Oct 21 '14 at 02:39
  • @DSM Will do. I think its not exactly lazy, but something else. – Games Brainiac Oct 21 '14 at 03:17
  • 1
    @GamesBrainiac: `property` is not designed to work with old-style classes. I've noticed in particular that `__set__()` never gets called. From the [python documentation on descriptors](https://docs.python.org/2.7/howto/descriptor.html?highlight=property#invoking-descriptors): "descriptors only work for new style objects and classes". This fact is repeated several times on the descriptor documentation page. – Joel Cornett Oct 21 '14 at 12:50
0

Because Python will 'name-mangle' double underscores. If you must use double underscores, then when you access them you have to do it as follows:

test._testClass__left

But instead you should just use a single underscore, which indicates a private variable, and you can continue on normally.

dursk
  • 4,435
  • 2
  • 19
  • 30
  • 1
    This isn't the issue here, though. – DSM Oct 21 '14 at 02:18
  • This doesn't seem to fix the issue. And besides, won't all the values be mangled? A single underscore still gives me an incorrect value. – ixwt Oct 21 '14 at 02:19
  • Ah, yes, @Parker got to it, I didn't even notice he forgot to inherit from `object` – dursk Oct 21 '14 at 02:19