2013-11-09 2 views
1

Я разрабатываю карточную игру в ООП только для практики, и у меня есть странное поведение в том, что я написал до сих пор. , . Когда я использую метод clear для опорожнения рук, происходит то, что отдельные руки показывают, что они пусты, но когда я смотрю на переменные руки (это показывает обе руки), они НЕ пусты. Мой вопрос, почему, LOL? Я поставлю код ниже и результаты ниже. , .Спасибо за любую помощь заранее.Класс Python Неожиданное поведение

import random 

class a: 
    my_hand = [] 
    other_hand = [] 
    hands = [my_hand, other_hand] 

    def h(self): 
     for rounds in range(5): 
      for hand in a.hands: 
       hand.append(rounds) 

    def shuffle(self): 
     random.shuffle(a.my_hand) 
     random.shuffle(a.other_hand) 

    def clear(self): 
     a.my_hand = [] 
     a.other_hand = [] 


x = a() 
x.h() 
x.shuffle() 


x.hands 
[[1, 0, 4, 2, 3], [3, 4, 2, 0, 1]] 

x.clear() 
[[1, 0, 4, 2, 3], [3, 4, 2, 0, 1]] 

x.my_hand 
[] 

x.other_hand 
[] 

x.hands 
[[1, 0, 4, 2, 3], [3, 4, 2, 0, 1]] 
+0

Часть кода в конце была отрезана. Он показал, что x.other_hand был [], тогда x.hands были [[1, 0, 4, 2, 3], [3, 4, 2, 0, 1]]. Извините –

ответ

1

Вы создаете новые пустые списки, но hands по-прежнему ссылается на старые.

Вы можете просто убедиться, что hands ссылается на новые пустые списки.

def clear(self): 
    a.my_hand = [] 
    a.other_hand = [] 
    a.hands = [a.my_hand, a.other_hand] 

или вы можете сами изменить сами списки, удалив все элементы.

def clear(self): 
    del a.my_hand[:] 
    del a.other_hand[:] 

FYI, все ваши функции определены как методы-члены, но ваши переменные всегда статичны. Вы можете ссылаться на self вместо a или сделать свои методы статическими (с @staticmethod).

+0

Я вижу. Делает совершенный смысл. Что конкретно сделало бы мои методы статичными (с декоратором) на самом деле? –

1

Как упражнение в ООП, ваш код странный ... вы используете члены класса в методах экземпляра. Экземпляр класса в методах называется self, а не a. Измените свой код на

def clear(self): 
    self.hand = [] 
    self.other_hand = [] 

и сделать то же самое для других частей кода.

Проблема вы столкнулись, однако, другое: при переназначении a.hand и a.other_hand вы создаете две новые пустые массивы, таким образом a.hands держит указывая на старых, созданных в h.

Изменение кода

a.hand[:] = [] 
a.other_hand[:] = [] 

или более идиоматических

del a.hand[:] 
del a.other_hand[:] 

решит эту проблему.

В Python синтаксис [:] означает «все элементы» и позволяет также нарезка (например a.hand[1:-1] список из elemets в a.hand, за исключением первого и последнего).

+0

Обратите внимание, что в python3 'list' есть метод clear() ', который, вероятно, более читаем, а затем' del + slicing'. – Bakuriu

+0

Это, наверное, странно, потому что я до сих пор ничего не писал в ООП. , . В любом случае, спасибо за помощь. –

+0

Оставляя код mu точно таким же, за исключением добавления того, что вы предложили a.hand [:] = [] и a.other_hand [:] = [], он отлично работал. Однако, как вы и другие говорили, я все еще ссылаюсь на a.hands, который указывает на старый список в h (как вы сказали выше), так почему он работает правильно сейчас? Разве это еще не работает, как в моем бывшем. выше и дать мне [[1, 0, 4, 2, 3], [3, 4, 2, 0, 1]]? –

3

В вашем a классе my_hand, other_hand и hands переменные являются все класса атрибутов (т.е. статических атрибутов), а не экземпляра атрибутов.

Также в вашем методе clear вы переназначить my_hand и other_hand, но hands по-прежнему ссылается на старые list с, следовательно, его содержание не меняется. Если вы используете python3, вы должны использовать метод clear()list s: my_hand.clear() и other_hand.clear(), затем hands будет ссылаться на два пустых list. На python2 вы должны сделать del my_hand[:].

Если вы хотите присвоить атрибут , вам необходимо сделать self.attribute = value. Для этого назначение должно быть помещено внутри метода __init__, который является конструктором класса.

Как он в настоящее время стоит ваш код, если вы создадите два экземпляра a, тогда они разделят руки, что, вероятно, то, что вы делаете не хотите.

Правильный код будет выглядеть так:

import random 

class ClassesUseCamelCase(object): 
    # if you are using python2 do *not* forget to inherit object. 
    def __init__(self): 
     self.my_hand = [] 
     self.other_hand = [] 
     self.hands = [self.my_hand, self.other_hand] 

    def init_hands(self): 
     for round in range(5): 
      for hand in self.hands: 
       hand.append(round) 
     # or simpler: 
     # for hand in self.hands: 
     #  hand.extend(range(5)) 

    def shuffle(self): 
     random.shuffle(self.my_hand) 
     random.shuffle(self.other_hand) 

    def clear(self): 
     self.my_hand.clear() # or del self.my_hand[:] in python2 
     self.other_hand.clear() 

Пример вывода с помощью IPython:

In [2]: inst = ClassesUseCamelCase() 

In [3]: inst.init_hands() 

In [4]: inst.shuffle() 

In [5]: inst.hands 
Out[5]: [[3, 2, 4, 0, 1], [3, 1, 4, 0, 2]] 

In [6]: inst.clear() 

In [7]: inst.hands 
Out[7]: [[], []] 

Заметьте, что он работает с более чем одним экземпляром:

In [9]: inst.init_hands() 

In [10]: other = ClassesUseCamelCase() 

In [11]: other.init_hands() 

In [12]: inst.hands, other.hands 
Out[12]: ([[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]) 

In [13]: inst.shuffle(); other.shuffle() 
    ...: 

In [14]: inst.hands 
Out[14]: [[0, 1, 3, 2, 4], [1, 4, 0, 3, 2]] 

In [15]: other.hands 
Out[15]: [[1, 4, 2, 0, 3], [1, 4, 3, 2, 0]] 

ваш код будет иметь разделили hands между двумя экземплярами.

Я помещаю здесь немного больше объяснений о том, что происходит, чтобы избежать написания слишком большого количества комментариев.

Прежде всего, в вашем коде my_hand и other_hand являются классом атрибутов. Почему это имеет значение? Это важно, потому что атрибуты класса являются общими экземпляров:

In [1]: class MyClass(object): 
    ...:  my_hand = [] 
    ...:  other_hand = [] 
    ...:  hands = [my_hand, other_hand] 
    ...:  

In [2]: instance_one = MyClass() 

In [3]: instance_two = MyClass() 

In [4]: instance_one.my_hand.append(1) 

In [5]: instance_two.my_hand # ops! 
Out[5]: [1] 

Вызова clear изменяет атрибуты класса и, следовательно, все экземпляры их использование. Это, обычно, это то, что вы делаете не хотите.

В экземплярах Test нет атрибута другого экземпляра, что означает, что все экземпляры на самом деле равны; все они действуют одинаково и предоставляют одни и те же данные. Единственное различие заключается в их личности. Если вы не собираетесь использовать личность, то наличие класса и нескольких экземпляров бесполезно, потому что вы могли бы просто использовать один объект.

Если вы хотите, чтобы экземпляры имели свои собственные данные, независимо от данных других экземпляров, вам необходимо использовать атрибуты экземпляра.

Об утверждении del. Как я уже сказал в комментариях, del имеет два разных вида использования. полный синтаксис оператора является:

del sequence, of, del_targets 

Если «последовательность из, del_targets» через запятую список того, что я буду называть del_targets. В зависимости от del_target поведение меняется.

Если это идентификтор, чем del удаляет, что ссылки из сферы, декремент счетчика ссылок на объект, который был передан идентификатором.

Например:

a = b = [] 
# Now that empty list has two references: a and b 
del b # now it has only one reference: a 
print(b) # This raises a NameError because b doesn't exist anymore 
del a  # Now the empty list doesn't have references 

Если объект не имеет ссылок он уничтоженные, поэтому после del a над пустым списком будет уничтожен интерпретатором.

Мишень также может быть «индексом», то есть выражением, например name[key-or-index] или name[a:slice] (или name[start:stop:step]). Синтаксис ломтиков (один с толстой кишкой) используется для указания диапазона индексов:

In [17]: numbers = list(range(10)) # [0, 1, ..., 9] 

In [18]: numbers[::2], numbers[1::2], numbers[2:7:3] 
Out[18]: ([0, 2, 4, 6, 8], [1, 3, 5, 7, 9], [2, 5]) 

При использовании del заявления с таким выражением Python вызывает __delitem__ метод объекта, проходя в индексе или срезе , Это означает, что:

del numbers[:] 

Средства: удалить все элементы списка numbers, которые соответствуют индексу в срезе :. Так как срез : означает «все индексы в последовательности», результат опустошает список. Обратите внимание, что он не удалите ссылку из списка. Он действует только на его элементы.

Вы можете получить тот же эффект с помощью:

numbers[:] = [] 

Это говорит питона, чтобы заменить последовательность элементов, соответствующих срезу : с элементами []. Поскольку : означает «все элементы» и [] пуст, эффект удаляет все элементы из списка. Этот синтаксис вызывает list.__setitem__ вместо list.__delitem__ под капотом, но результат тот же. Однако вы также можете сделать: numbers[:] = [2], а затем все элементы numbers будут удалены и2 будет вставлен в результате списка [2].

Оба работают, однако я предпочитаю синтаксис del, потому что он делает явные ваши намерения. Когда вы читаете:

del something[:] 

Вы знаете, что заявление будет удалить что-нибудь. Затем вы видите индекс [:], и вы понимаете, что он удалит элементы из something, а не ссылку something. Однако, когда вы видите:

something[:] = [] 

Сначала вы думаете, хорошо это задание. Затем вы видите [:], и вы понимаете, что делаете «переписываете» содержимое списка. Затем вы смотрите на правую сторону и видите [], поэтому мы собираемся перезаписать элементы пустым списком ... наконец, вы поймете, что оператор просто удалит все элементы из списка.

+0

Большое спасибо. –

+0

Да, у вас есть смысл. Я в основном следовал примеру из хорошо известной книги Python. Я не совсем уверен, почему он использовал переменные класса. –

+0

Не использовал бы «del self.my_hand []» и т. Д. Полностью удалить список? Другими словами, я хочу сохранить список сам, но сделать его пустым. Или я ошибаюсь в отношении того, что на самом деле делает «del»? –

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