2017-01-17 11 views
2

У меня есть класс TextBuffer. Он имеет функцию __init__, которая может использоваться для передачи начального значения для содержимого TextBuffer. Если значение не передано, оно должно использовать пустой список как значение по умолчанию.Метод __init__ получает значение, пока ни один не передан

Класс имеет метод добавить, чтобы добавить текст в TextBuffer:

a = TextBuffer() 
a += "TEXT" 

По какой-то причине вызова метода __init__ без параметров не использует значение по умолчанию [] для lst, но ранее добавленную стоимость другого экземпляра TextBuffer, которое не находится в __init__ вызова

a = TextBuffer() 
a += "BUFFER A" 
b = TextBuffer() ### lst in the __init__ call has the value of ["BUFFER A"] 
b += "BUFFER B" 

Я понятия не имею, почему это происходит. Может быть, я делаю что-то не так в методе __add__?

Пожалуйста, обратите внимание полный пример кода:

import pprint 

### Two functions for byte/string conversion in Python3 
def b(x): 
    if (type(x) == type(b'')): 
     return x 
    else: 
     return x.encode(encoding='UTF-8') 
def s(x): 
    if (type(x) == type(b'')): 
     return x.decode(encoding='UTF-8') 
    else: 
     return x 


class TextBuffer(): 
    def __init__(self, lst = [], maxlines = None): 
     self.content = [] 
     if (type(lst) == type([])): 
      if (len(lst) > 0): 
       print("INIT got a nonempty lst value:") 
       pprint.pprint(lst) 
       print("------------------------------") 
      self.content = lst 


    def __getitem__(self, i): 
     try: 
      return self.content[i] 
     except IndexError: 
      return None 

    def __iter__(self): 
     for line in self.content: 
      yield line 

    def __contains__(self, item): 
     return item in self.content 

    def __len__(self): 
     return len(self.content) 

    def __add__(self, item): 
     self.content.append(item) 
     return self 

    def __radd__(self, item): 
     return self.__add__(item) 

    def __repr__(self): 
     result = "" 
     for line in self.content: 
      if (type(line) == type(b"")): 
       result+=s(line) 
      else: 
       result+=line 
     return result 

    def __str__(self): 
     return repr(self) 

    def __unicode__(self): 
     return repr(self.content) 

### TextBuffer INIT with empty list creates an empty textbuffer 
a = TextBuffer(lst=[]) 
print("a = TextBuffer(lst=[])") 

a += "BUFFER A" 


### TextBuffer INIT with empty list creates an empty textbuffer 
b = TextBuffer(lst=[]) 
print("b = TextBuffer(lst=[])") 

b += "BUFFER B" 

print("Content of TextBuffer a:") 
print(a) 
print("Content of TextBuffer b:") 
print(b) 

print("-------------------------") 

### TextBuffer INIT without any parameters should use default value for lst of [] 
### So an empty list. Should be the same as TextBuffer(lst=[]) 
a = TextBuffer() 
print("a = TextBuffer()") 

a += "BUFFER A" 

### TextBuffer INIT without any parameters should use default value for lst of [] 
### So an empty list. Should be the same as TextBuffer(lst=[]) 
### But now, the value of lst is not [], it is the string added to TextBuffer 'a': ['BUFFER A'] 
b = TextBuffer() 
print("b = TextBuffer()") 

b += "BUFFER B" 

print("Content of TextBuffer a:") 
print(a) 
print("Content of TextBuffer b:") 
print(b) 
+0

Поскольку у вас есть изменяемая вещь как аргумент по умолчанию ('[]'). Все экземпляры разделяют _that_ list, поэтому, если один из них добавляет к нему элемент, все они добавляются. Это дубликат многих вопросов. – RemcoGerlich

+0

http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#default-parameter-values ​​ – RemcoGerlich

+0

Там _are_ ситуации, где вы можете безопасно использовать изменяемый по умолчанию аргумент, это не один из них. –

ответ

2

Вы не можете использовать lst = []в качестве аргумента по умолчанию в этом случае, так как Python будет делает один список в память и передает каждый раз ссылку к тот же объект (список) конструктору. Это означает, что ваши два объекта имеют одинаковые lst, и, таким образом, изменения, сделанные одним объектом, отражаются в другом и наоборот.

Вы можете использовать следующий трюк:

def __init__(self, lst = None, maxlines = None): 
    if lst is None: 
     lst = [] 
    #... (remainder of the constructor) 

так как здесь вы вынуждаете Python построить новый список. Очевидно, вы могли бы использовать другое значение, чем None; но я думаю, что для вашего дела это будет достаточно.

В целом это хорошая идея, чтобы не корма изменяемые объектов в качестве значений по умолчанию, всегда использовать неизменные объекты. Тем не менее, поскольку @hiroprotagonist описывает случаи, когда это поведение требуется (например, memoization), но я бы советовал быть очень осторожным с ними.

+4

вы * можете * использовать изменяемый тип в качестве аргумента по умолчанию. если вы знаете, что вы делаете, это может быть хорошей идеей (например, meomize). –

+0

Когда список перешел на инстас? при загрузке модуля или при вызове mehtod '__init__'? – Netwave

+0

@hiroprotagonist да, но, как правило, это не то, что вы хотите сделать. –

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

  • Нет связанных вопросов^_^