Нежелательная реликвия экземпляра питателя питона
Вопрос: Как мне убить экземпляр или убедиться, что я создаю новый экземпляр универсального фидпарсера python?
Информация:
Я сейчас работаю над программой, которая загружает и каталогизирует большое количество блогов. Это хорошо сработало, за исключением неудачной ошибки. Мой код настроен так, чтобы брать список URL блогов и запускать их через цикл for. при каждом запуске он выбирает URL и отправляет его в отдельный класс, который управляет загрузкой, извлечением и сохранением данных в файл.
Первый URL работает просто отлично. Он загружает весь блог и сохраняет его в файл. Но второй загружаемый блог будет содержать все данные первого, я совершенно не знаю, почему.
Фрагменты кода:
class BlogHarvester:
def __init__(self,folder):
f = open(folder,'r')
stop = folder[len(folder)-1]
while stop != '/':
folder = folder[0:len(folder)-1]
stop = folder[len(folder)-1]
blogs = []
for line in f:
blogs.append(line)
for herf in blogs:
blog = BlogParser(herf)
sPath = ""
uid = newguid()##returns random hash.
sPath = uid
sPath = sPath + " - " + blog.posts[0].author[1:5] + ".blog"
print sPath
blog.storeAsFile(sPath)
class BlogParser:
def __init__(self, blogherf='null', path='null', posts = []):
self.blogherf = blogherf
self.blog = feedparser.parse(blogherf)
self.path = path
self.posts = posts
if blogherf != 'null':
self.makeList()
elif path != 'null':
self.loadFromFile()
class BlogPeices:
def __init__(self,title,author,post,date,publisher,rights,comments):
self.author = author
self.title = title
self.post = post
self.date = date
self.publisher = publisher
self.rights = rights
self.comments = comments
Я включил фрагменты, которые, как мне показалось, было бы полезно. Извините, если есть какие-то запутанные артефакты. Эта программа была болью в заднице.
2 ответа
Как сказал Игнасио, любые мутации, которые происходят с аргументами по умолчанию в списке функций, будут оставаться на всю жизнь класса.
От http://docs.python.org/reference/compound_stmts.html
Значения параметров по умолчанию оцениваются при выполнении определения функции. Это означает, что выражение вычисляется один раз, когда функция определена, и что одно и то же "предварительно вычисленное" значение используется для каждого вызова. Это особенно важно понимать, когда параметр по умолчанию является изменяемым объектом, таким как список или словарь: если функция изменяет объект (например, путем добавления элемента в список), значение по умолчанию фактически изменяется. Это вообще не то, что было задумано. Чтобы обойти это, нужно использовать None как значение по умолчанию и явно проверить его в теле функции.
Но это вызывает некоторую ошибку: вы изменяете ссылку... Таким образом, вы можете изменять список, который потребитель класса, который не должен был быть изменен:
Например:
class A:
def foo(self, x = [] ):
x.append(1)
self.x = x
a = A()
a.foo()
print a.x
# prints: [1]
a.foo()
print a.x
# prints: [1,1] # !!!! Consumer would expect this to be [1]
y = [1,2,3]
a.foo(y)
print a.x
# prints: [1, 2, 3, 1]
print y
# prints: [1, 2, 3, 1] # !!!! My list was modified
Если вы вместо этого скопируете его: (См. http://docs.python.org/library/copy.html)
import copy
class A:
def foo(self, x = [] ):
x = copy.copy(x)
x.append(1)
self.x = x
a = A()
a.foo()
print a.x
# prints: [1]
a.foo()
print a.x
# prints: [1] # !!! Much better =)
y = [1,2,3]
a.foo(y)
print a.x
# prints: [1, 2, 3, 1]
print y
# prints: [1, 2, 3] # !!!! My list is how I made it
Проблема в posts=[]
, Аргументы по умолчанию вычисляются во время компиляции, а не во время выполнения, поэтому мутации в объекте остаются в течение всего времени жизни класса. Вместо этого используйте posts=None
и проверить:
if posts is None:
self.posts = []