2015-01-29 7 views
-1

Это будет простой вопрос, так что вам не потребуется много времени, чтобы объяснить это мне, но это поможет мне (и, возможно, другим людям тоже) понять что-то о классах и методах и Python.экземпляр не имеет атрибута '__len__'

Мой код хочет представить настольную игру на этом уровне только на доске с N отверстиями в строке, которая может вмещать мрамор, пронумерованный от 0 до N-1 (в случайном порядке).

class MarblesBoard: 
    '''define the board game''' 
    def __init__(self, Board): 
     self._Board = Board 
     self._n = len(Board) 

    '''first type of move available: switch the first two marbles''' 
    def switch(Board): 
     Board[0], Board[1] = Board[1], Board[0] 
     return Board 

    '''second type of move available: move all the marbles by 1 space to the left''' 
    def rotate(): 
     temp = Board[0] 
     for i in range (0, n-1): 
      Board[i] = Board[i+1] 
     Board[n-1] = temp 
     return Board 

    '''print the state''' 
    def __repr__(Board): 
     for i in range(0, len(Board)): 
      print self[i], 

    '''rest to be developed when this works''' 

сейчас, я проверяю отображение доски и перемещение мрамора. Так что пусть

First = MarblesBoard([1,2,3,4]) 
First.rotate 

и получить только

<repr(<instancemethod at 0x104230410>) failed: AttributeError: MarblesBoard instance has no attribute '__len__'> 

Я знаю, что уже подобный вопрос об этом сообщении об ошибке. AttributeError: Entry instance has no attribute '__len__'

Я все еще как-то не вижу, как возникает одна и та же ошибка в моем коде. Не могли бы вы объяснить (в общем), когда установлен атрибут «len» и, может быть, предложите, как это можно сделать в этом коде?

+1

Вам нужно 'def __repr __ (self): для i в диапазоне (0, len (self._Board)): print i' Метод' __repr__' должен находиться в вашем классе 'Board'. Далее каждый из методов 'switch',' rotate' должен быть определен как 'switch (self)', 'rotate (self)' и работать на 'selfBoard' – linuxfan

+0

Я не понимаю, почему вы пытаетесь получить доступ 'len' на классе. Разве вы не должны использовать экземпляр? Кроме того, не должны ли все методы класса быть соответствующим образом отступом из инструкции 'class MarblesBoard:'? –

ответ

1
def __init__(self, Board): 
    self._Board = Board 
    self._n = len(Board) 

'''first type of move available: switch the first two marbles''' 
def switch(Board): 
    Board[0], Board[1] = Board[1], Board[0] 
    return Board 

Вы, кажется ожидать, что Board в методе switch будет таким же Board a s в __init__ - это не так. Первым аргументом для любого метода является экземпляр, с которым вы работаете (например, экземпляр MarblesBoard, когда вы ожидаете, что это список) - имя, которое вы ему даете, не влияет на это, но оно условно (как вы правильно сделали в __init__) назовите его self.

Board в __init__ - это аргумент, который вы фактически передали - список.Чтобы вернуть его в других методах, вам необходимо сохранить его рядом с self - вы уже это сделали, его назвали self._Board. Как и любое другое имя переменной или аргумент функции, имя, которое вы выберете, не повлияет на функциональность, но - снова - есть соглашения и вызывать его self._board (все строчные буквы) вместо этого будут более обычными. Таким образом, вы можете переписать приведенный выше два метода, как:

def __init__(self, board): 
    self._board = board 
    self._n = len(Board) 

'''first type of move available: switch the first two marbles''' 
def switch(self): 
    self._board[0], self._board[1] = self._board[1], self._board[0] 

(обратите внимание, вы, вероятно, не хотите ничего возвращать из switch теперь, когда он изменяет состояние self).

Это подводит нас к сути проблемы. Python неявно вызывая __repr__ метод, который вы определили, как это:

def __repr__(Board): 
    for i in range(0, len(Board)): 
     print self[i], 

То же самое происходит: вы ожидаете Board быть список, когда это MarblesBoard. Вызов len(Board) затем пытается позвонить MarblesBoard.__len__, которого не существует. Вы также попробовали позвонить self[i], который также был бы ошибкой, если бы не был он перед ним (потому что он вызывает другой магический метод, MarblesBoard.__getitem__, который вы также не определили). Способ исправить обе проблемы сразу, чтобы делать то, что мы делали выше и попасть в список через экземпляр MarblesBoard:

def __repr__(self): 
    for i in range(0, len(self._board)): 
     print self._board[i] 

будет работать. Однако обратите внимание, что эта формулировка по-прежнему не очень Pythonic - как только ваш код работает, вы можете захотеть опубликовать его на codereview.SE.

+0

Вызов 'print' внутри' __repr__' - это не то, как он должен работать. Вы должны ** возвращать ** строку. – b4hand

+0

@ b4hand это правда, но обратите внимание, что эта ошибка напрямую копируется из вопроса. Я сознательно не исправлял все нетрадиционные вещи, а вместо этого подталкивал его к кодосмотру, потому что ответ уже немного перегрузился. – lvc

1

Когда вы делаете:

First = MarblesBoard([1,2,3,4]) 
First.rotate 

__repr__ называется, так что представление объекта может быть эхом на терминале. Вот ваше (обратите внимание, что печать в это неправильно, вы должны просто вернуть строку, но исправление, что выходит за рамки этого.):

def __repr__(Board): 
    for i in range(0, len(Board)): 
     print self[i], 

Вы называете len(Board). Этот аргумент, Board, фактически является экземпляром объекта. Вместо этого Pythonic использует self. Вы не определили функцию __len__ для len, чтобы позвонить по этому объекту, и таким образом вы получите сообщение об ошибке.


Вот пример некоторых изменений, которые я бы сделал. Во-первых наследоваться от объекта:

class MarblesBoard(object): 
    '''define the board game''' 

Далее, мы только прописными буквами имена классов:

def __init__(self, board): 
     self._board = board 
     self._n = len(board) 

Наконец, строки документации идут под именем функции и аргументы:

def switch(self): 
     '''first type of move available: switch the first two marbles''' 
     self.board[0], self.board[1] = self.board[1], self.board[0] 
     return self.board 
+0

Вызов 'print' внутри' __repr__' является опасным и неправильным. – b4hand

+0

Спасибо, Аарон! Только один последний вопрос: какова цель определения класса MarblesBoard (объект):? Может ли он использоваться для определения массива платы? Когда я пытаюсь это сделать, я получаю TypeError: object() не принимает параметров –

+0

@LochnessMonster. Я бы использовал более простой подход к тому, чтобы сделать массив атрибутом объекта, таким образом вы можете более легко изменить реализацию. См. Модель данных Python для получения дополнительных возможностей настройки объектов (https://docs.python.org/2/reference/datamodel.html#basic-customization), но я бы сохранил ее просто, пока вы учитесь. –

Смежные вопросы