2015-01-01 2 views
1

Я пишу сценарий для отправки статистики из текстового файла в Markdown. Файл содержит названия книг и даты. Каждая дата относится к следующим заголовкам, пока не появится новая дата. Вот пример:Нечетное поведение петли в Python

#### 8/23/05 
Defining the World (Hitchings) 
#### 8/26/05 
Lost Japan 
#### 9/5/05 
The Kite Runner 
*The Dark Valley (Brendon)* 
#### 9/9/05 
Active Liberty 

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

Существует два исключения: файл начинается с заголовков, а не с даты, поэтому я устанавливаю начальное значение для this_date перед циклом for. И на полпути через файл есть область, где даты были потеряны, и я установил конкретную дату для этих названий.

Но в результирующем списке dicts все заголовки даны этой дате до начала области потерянных данных. После этого в остальной части заголовков указывается дата, которая появляется последним в файле. Самое смутное: когда я печатаю содержимое this_date непосредственно перед добавлением нового dict, он содержит правильное значение для каждого цикла.

Я ожидаю, что this_date будет виден на всех уровнях цикла. Я знаю, что мне нужно разбить это на функции, и передача результатов явно между функциями, вероятно, устранит проблему, но я хотел бы знать, почему этот подход не сработал. Большое спасибо.

result = [] 

# regex patterns 
ddp = re.compile('\d+') # extract digits 
mp = re.compile('^#+\s*\d+') # captures hashes and spaces 
dp = re.compile('/\d+/') # captures slashes 
yp = re.compile('\d+$') 
sp = re.compile('^\*') 

# initialize 
this_date = { 
    'month': 4, 
    'day': 30, 
    'year': 2005 
} 
# print('this_date initialized') 

for line in text: 
    if line == '': 
     pass 
    else: 
     if '#' in line: # markdown header format - line is a new date 
      if 'Reconstructing lost data' in line: # handle exception 
      # titles after this line are given 12/31/14 (the last date in the file) instead of 8/31/10 
      # all prior dates are overwritten with 8/31/10 
      # but the intent is that titles after this line appears have date 8/31/10, until the next date 
       this_date = { 
        'month': 8, 
        'day': 31, 
        'year': 2010 
       } 
       # print('set this_date to handle exception') 
      else: # get the date from the header 
       month = ddp.search(mp.search(line).group()) # digits only 
       day = ddp.search(dp.search(line).group()) # digits only 
       year = yp.search(line) 
       if month and day and year: 
        # print('setting this_date within header parse') 
        this_date['month'] = int(month.group()) 
        this_date['day'] = int(day.group()) 
        this_date['year'] = (int(year.group()) + 2000) 
       else: 
        pass 
     else: # line is a title 
      x = { 
       'date': this_date, 
       'read': False 
       } 
      if sp.match(line): # starts with asterisk - has been read 
       x['read'] = True 
       x['title'] = line[1:-3] # trim trailing asterisk and spaces 
      else: 
       x['title'] = line 
      # this_date is correct when printed here 
      # print('this_date is ' + str(this_date['month']) + '/' + str(this_date['day']) + '/' + str(this_date['year'])) 
      result.append(x) 
      # x has correct date when printed here 
      # print(x) 

# print("Done; found %d titles.") % len(result) 
# elements of result have wrong dates (either 8/31/10 or 12/31/14, no other values) when printed here 
# print(result[0::20]) 

ответ

2

Вы создаете this_date словарь только один раз. You then повторное использование этот словарь каждый цикл итерация. Вы добавляете только ссылки к этому словарю в список result; это только один словарь, на который ссылаются снова и снова.

магазин новый копия словаря каждый цикл итерации:

x = { 
    'date': this_date.copy(), 
    'read': False 
    } 

Ваш код мог бы сделать с некоторым упрощением; Вместо этого я использовал бы datetime.date() objects, поскольку они правильно моделируют даты. Нет регулярных выражений не требуются:

from datetime import datetime 

current_date = None 
results = [] 
for line in text: 
    line = line.strip() 
    if not line: 
     continue 

    if line.startswith('#'): 
     current_date = datetime.strptime(line.strip('# '), '%m/%d/%y').date() 
     continue 

    entry = {'date': current_date, 'read': False} 

    if line.startswith('*') and line.endswith('*'): 
     # previously read 
     line = line.strip('*') 
     entry['read'] = True 

    entry['title'] = line 
    results.append(entry) 

Потому что datetime.date() объектов являются неизменными, и мы создаем новый date объект каждый раз, когда мы сталкиваемся строкой заголовка, вы можете безопасно повторно использовать последнюю читаемую дату.

Демо:

>>> from datetime import datetime 
>>> from pprint import pprint 
>>> text = '''\ 
... #### 8/23/05 
... Defining the World (Hitchings) 
... #### 8/26/05 
... Lost Japan 
... #### 9/5/05 
... The Kite Runner 
... *The Dark Valley (Brendon)* 
... #### 9/9/05 
... Active Liberty 
... '''.splitlines(True) 
>>> current_date = None 
>>> results = [] 
>>> for line in text: 
...  line = line.strip() 
...  if not line: 
...   continue 
...  if line.startswith('#'): 
...   current_date = datetime.strptime(line.strip('# '), '%m/%d/%y').date() 
...   continue 
...  entry = {'date': current_date, 'read': False} 
...  if line.startswith('*') and line.endswith('*'): 
...   # previously read 
...   line = line.strip('*') 
...   entry['read'] = True 
...  entry['title'] = line 
...  results.append(entry) 
... 
>>> pprint(results) 
[{'date': datetime.date(2005, 8, 23), 
    'read': False, 
    'title': 'Defining the World (Hitchings)'}, 
{'date': datetime.date(2005, 8, 26), 'read': False, 'title': 'Lost Japan'}, 
{'date': datetime.date(2005, 9, 5), 
    'read': False, 
    'title': 'The Kite Runner'}, 
{'date': datetime.date(2005, 9, 5), 
    'read': True, 
    'title': 'The Dark Valley (Brendon)'}, 
{'date': datetime.date(2005, 9, 9), 'read': False, 'title': 'Active Liberty'}] 
+0

Кроме того, создать новый Dict вместо модификации старой. Тогда нет необходимости копировать его сотни раз. –

+0

@Rawing: OP упомянул, что они ожидают, что дата будет использоваться повторно в последующих итерациях. –

+0

Оба подхода работали. Что такое компромисс между вставкой копии dict 'date': this_date.copy() и созданием нового dict 'date': { 'month': this_date ['month'], 'day': this_date [ 'day'], 'year': this_date ['year'] } – user2014160

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