Ошибка атрибута при создании объекта с атрибутом кортежа

#python

Вопрос:

Я только начал работать с ООП в Python, и меня попросили создать класс с именем Square с определенным атрибутом position, который имеет тип tuple. Таким образом, для каждого квадратного объекта существует определенный метод его печати с учетом атрибута размера (печатает » # » размера*размер) и атрибута позиции (начинается с координат (x, y)), но когда у меня есть ошибка атрибута в методе инициализации, которая сообщает мне:

Ошибка атрибута: объект «Квадрат» не имеет атрибута «_Квадрат__позиция»

Вот код:

 class Square:
''' create Square instance with public attributes
size and position '''
def __init__(self, size=0, position=(0, 0)):
    self.size = size
    self.position = position

''' retrieve size and make it private'''
@property
def size(self):
    return self.__size

''' set private size attribute '''
@size.setter
def size(self, value):
    if type(value) is not int:
        raise TypeError("size must be an integer")
    if value < 0:
        raise ValueError("size must be >= 0")
    self.__size = value

''' retrieve position and make it private '''
@property
def position(self):
    return self.__position

''' set private position attribute '''
@position.setter
def position(self, value):
    if value[0] < 0 or value[1] < 0:
        raise TypeError("position must be a tuple of 2 positive integers")
    self.__position[0] = value[0]
    self.__position[1] = value[1]

''' calculates area of square '''
def area(self):
    return self.__size ** 2

''' print a square of # the size of self.__size'''
def my_print(self):
    if self.__size == 0:
        print()
    else:
        for line in range(self.__position[1]):
            print()
        for i in range(self.__size):
            for space in range(self.__position[0]):
                print(" ", end="")
            for j in range(self.__size):
                print('#', end="")
            print()
 

Любая помощь приветствуется, спасибо!

Комментарии:

1. Пожалуйста, опубликуйте всю обратную связь, чтобы мы видели поток.

2. У вас есть вторая проблема, связанная с первой. Кортежи неизменны. Ты хочешь __position быть кортежем? Если это так, то вы не можете этого сделать self.__position[0] = value[0] . Скорее всего, вы хотите просто self.__position = value заняться сеттером. Но тогда почему это вообще должно быть собственностью?

3. свойства стоят дороже, чем переменные экземпляра. Они полезны, если вы хотите запустить дополнительный код при получении или настройке (возможно, вы хотите проверить, что позиция является кортежем, прежде чем принимать ее). В противном случае они вам не понадобятся. «Частные» переменные могут быть проблематичными. При использовании 2 символов подчеркивания имя переменной искажается вместе с именем класса. Это затрудняет наследование.

4. Искажение имени (двойной префикс подчеркивания) не делает атрибут «частным». Нет способа сделать атрибут личным. Вместо этого используйте один префикс подчеркивания, чтобы указать, что атрибут не является общедоступным API.

Ответ №1:

Ваш __init__() метод должен быть таким:

 def __init__(self, size=0, position=(0, 0)):
    self.__size = size    #<-- dunder
    self.__position = position #<-- dunder
 

Редактировать

Спасибо @tdelaney, который указал, что position это кортеж, который не позволяет присваивать элементы. Итак, вы @position.setter должны быть такими:

 @position.setter
def position(self, value):
    if type(value) != tuple or value[0] < 0 or value[1] < 0:
        raise TypeError("position must be a tuple of 2 positive integers")
    self.__position = value
 

Комментарии:

1. Я не могу поверить, что это было просто так. Поскольку я сделал атрибут закрытым с помощью установщика свойств, я подумал, что он может быть общедоступным в функции инициализации. Это сработало, спасибо!

2. @Anwarvic -Это, скорее всего, не ответ. Вы увидите сбой, если попытаетесь позже снова установить позицию — возможно, вы захотите переместить квадрат и сделать square.position = (3,4) это позже. Это не удастся. Как только вы это исправите, ваш оригинал __init__ также снова начнет работать.

3. @tdelaney ты прав… давайте исправим это для него/нее.

4. Мне не нравится это решение, потому что вы все еще предлагаете использовать искажение имен без веской причины.

5. @wim — Я согласен, что это, скорее всего, не нужно, но я думаю, что это дизайнерское решение вне строгого ответа на вопрос. Ответы не обязательно должны касаться того, о чем не спрашивают.