1

I'm fairly new to Python and I'm working with classes(in Python) for the first time. When I try to use the constructor, I get an AttributeError, though I'm not really sure why this is occurring.

class Pawn(object):

    def __init__(self, x: int, y: int):
        if (not x in range(1,9)):
            raise ValueError('Invalid X-Position')
        if (not y in range(1,9)):
            raise ValueError('Invalid Y-Position')
        self.x = x
        self.y = y

    @staticmethod
    def parseFromString(sPawn):
        try:
            return Pawn(ord(sPawn[0]) - 96, int(sPawn[1]))
        except ValueError:
            raise ValueError('Parse Failed. Please check pawn string.')

    def getLethals(self):
        lethals = {}

        try:
            lethals.append(Pawn(self.x - 1, self.y))
            lethals.append(Pawn(self.x + 1, self.y))
            lethals.append(Pawn(self.x, self.y - 1))
            lethals.append(Pawn(self.x, self.y + 1))
            lethals.append(Pawn(self.x - 1, self.y - 1))
            lethals.append(Pawn(self.x + 1, self.y + 1))
            lethals.append(Pawn(self.x - 1, self.y + 1))
            lethals.append(Pawn(self.x + 1, self.y - 1))
        except ValueError:
            pass

        return lethals

    def packToString(self):
        return str(chr(self.x + 96)) + str(self.y)

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

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

When I try to create an instance:

def testFunc():
    pawn = Pawn(2,4)

I get an:

AttributeError: can't set attribute

Tracing all the way back to 'self.x = x' of the constructor of 'Pawn'.

EDIT: I didn't understand that properties revoked writing privileges of class variables. I will add setters.

ashabbir
  • 21
  • 2
  • 1
    Yes, because `x` is a `property` without a setter. Why did you expect to be able to set it? Really, the only point of even creating such properties *is to prevent them from being set*, so why are you using `property` at all – juanpa.arrivillaga Jan 03 '19 at 03:13
  • Possible duplicate of [Using @property versus getters and setters](https://stackoverflow.com/questions/6618002/using-property-versus-getters-and-setters) – James Jan 03 '19 at 03:15
  • When you used `@property` with `def x(self)`, you forced `x` to be read-only unless you also use `@x.setter` with `def x(self, value)`. – James Jan 03 '19 at 03:17
  • In this case, you aren't controlling access, so just don't use `property` normal attributes will do and should be preferred – juanpa.arrivillaga Jan 03 '19 at 03:19
  • @James Thank you for the clarification, I didn't have a good understanding about how properties worked in python. – ashabbir Jan 03 '19 at 03:43

1 Answers1

-1

Try this:

  • Use other internal names for x and y namely _x and _y. Once inside the class we use _x and _y while once outside we use x and y.
  • Added setters for x and y.

Code:

class Pawn(object):
    def __init__(self, x: int, y: int):
        if (not x in range(1,9)):
            raise ValueError('Invalid X-Position')
        if (not y in range(1,9)):
            raise ValueError('Invalid Y-Position')
        self._x = x
        self._y = y

    @staticmethod
    def parseFromString(sPawn):
        try:
            return Pawn(ord(sPawn[0]) - 96, int(sPawn[1]))
        except ValueError:
            raise ValueError('Parse Failed. Please check pawn string.')

    def getLethals(self):
        lethals = {}

        try:
            lethals.append(Pawn(self._x - 1, self._y))
            lethals.append(Pawn(self._x + 1, self._y))
            lethals.append(Pawn(self._x, self._y - 1))
            lethals.append(Pawn(self._x, self._y + 1))
            lethals.append(Pawn(self._x - 1, self._y - 1))
            lethals.append(Pawn(self._x + 1, self._y + 1))
            lethals.append(Pawn(self._x - 1, self._y + 1))
            lethals.append(Pawn(self._x + 1, self._y - 1))
        except ValueError:
            pass

        return lethals

    def packToString(self):
        return str(chr(self._x + 96)) + str(self._y)

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

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

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

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

pawn = Pawn(2,4)
print(pawn.x)
print(pawn.y)
duong_dajgja
  • 4,196
  • 1
  • 38
  • 65
  • 1
    This is *not* how you should be using `property`. First, you shouldn't use double-underscore name-mangling unless that is actually what you want, which it is not. Rather, you want a *single* underscore to signify "private by convention". Second, your properties are totally pointless, they don't do anything, they should just be attributes – juanpa.arrivillaga Jan 03 '19 at 03:50
  • @juanpa.arrivillaga Wouldn't this violate encapsulation? Coming from a java background, it feels wrong accessing an instance variable without using a getter a or a setter. – ashabbir Jan 03 '19 at 04:24
  • @juanpa.arrivillaga Updated `__` to `_` convention. Re. properties related things, I would let the OP decide whether he wants or not. I am just providing an approach for him. – duong_dajgja Jan 03 '19 at 04:42
  • @ashabbir no, the beauty of `property` and the descriptor protocol more generally is that they provide encapsulation *without boilerplate*. Read the following: https://www.python-course.eu/python3_properties.php – juanpa.arrivillaga Jan 03 '19 at 10:07