0

While handling the database connection I used the singleton pattern for the evident reason. for simplification purposes, I have simplified the class definition, the problem is still the same.

the class:

class Point(object):
    _instance = None

    def __new__(cls, x, y):
        if Point._instance is None:
            Point._instance = object.__new__(cls)
            Point._instance.x = x
            Point._instance.y = y
        return Point._instance

    def __init__(self, x, y):
        self.x = x
        self.y = y

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, x):
        self._x = self._instance.x

    @property
    def y(self):
        return self._y

    @y.setter
    def y(self, y):
        self._y = self._instance.y

    def __str__(self):
        return 'x: {}, y: {} id.x: {}'.format(self.x, self.y, id(self.x))

it generates the following error:

AttributeError: 'Point' object has no attribute '_x'

I have found the following workaround:

class Point(object):
    _instance = None

    def __new__(cls, x, y):
        if Point._instance is None:
            Point._instance = object.__new__(cls)
            Point._instance.x = x
            Point._instance.y = y
        return Point._instance

    def __init__(self, x, y):
        self.x = self._instance.x
        self.y = self._instance.y

The pythonic way is to use property method, hence I still have that itch, even though I have a working code, can someone explain to me why-why I have such an error.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
rachid el kedmiri
  • 2,376
  • 2
  • 18
  • 40

2 Answers2

1

When invoking self.x in your __init__, control (via the descriptor) is moves to the setter for x which does:

self._x = self._instance.x

which, in turn, invokes the getter which tries to do:

return self._x 

before self._x has been set. A similar situation exists for _y.

I'm under the impression you don't want people altering the values for x and y, if that is the case, make them read-only properties.

As an addendum, there's no reason to set the values x and y in __new__, you set them in __init__.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
  • as mentioned in my question, the original class is for handling database connection, hence I prevent multiple instantiations, and please bear with me and imagine x as the connection and y as the cursor. – rachid el kedmiri Sep 26 '17 at 03:09
1

Although I'm not sure I understand why you would want to do this, you could try:

_instance = None

def Point(x,y):
    class _Point(object):
        def __init__(self, x, y):
            self.x = x
            self.y = y
        def __str__(self):
            return 'x: {}, y: {} id.x: {}'.format(self.x, self.y, id(self.x))
    global _instance
    if _instance is None:
        _instance = _Point(x,y)
    return _instance    

p1 = Point(1,2)
print "p1", p1

p2 = Point(3,4)
p2.x = 10
print "p2", p2

print "p1", p1

Output

p1 x: 1, y: 2 id.x: 94912852734312
p2 x: 10, y: 2 id.x: 94912852734096
p1 x: 10, y: 2 id.x: 94912852734096

Try it online!

jq170727
  • 13,159
  • 3
  • 46
  • 56