Значения из атрибута экземпляра класса, добавляемого к другому экземпляру того же класса

Я анализирую PDF для извлечения данных таблицы, используя мой класс PdfTable. Когда я создаю экземпляр класса, затем создаю другой экземпляр класса, создается впечатление, что первый экземпляр класса file_1.cells добавляется перед вторым экземпляром класса file_2.cells. Я не могу понять, почему это происходит, потому что я не думаю, что я создаю переменные класса, а только переменные экземпляра. По какой-то причине данные из set_cells сохраняются, когда создается экземпляр другого класса. Что происходит?

from pdfminer.pdfdocument import PDFDocument
from pdfminer.pdfpage import PDFPage
from pdfminer.pdfparser import PDFParser
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import PDFPageAggregator
from pdfminer.layout import LAParams, LTTextBox, LTTextBoxHorizontal, LTTextLineHorizontal
from tabulate import tabulate
from utils import clean_string
from collections import namedtuple


class PdfTable(object):

    def __init__(self, file_name):
        self.file_name = file_name
        self.table_headers = None
        self.cells = None
        self.rows = None

    def process_file(self, file_name):
        pages = []
        with open(file_name, 'rb') as fp:
            parser = PDFParser(fp)
            doc = PDFDocument(parser)
            rsrcmgr = PDFResourceManager()
            laparams = LAParams()
            device = PDFPageAggregator(rsrcmgr, laparams=laparams)
            interpreter = PDFPageInterpreter(rsrcmgr, device)

            for page in PDFPage.create_pages(doc):
                interpreter.process_page(page)
                pages.append(device.get_result())

        return pages

    def set_table_headers(self, page_obj, table_headers={}):
        values = ('NAME', 'VALUE', 'REFERENCE RANGE')
        Header = namedtuple('Header', 'name, x0, y0')

        for obj in page_obj:
            if isinstance(obj, LTTextLineHorizontal):
                text = clean_string(obj.get_text())
                if text in values:
                    table_headers[text] = Header(text, obj.bbox[0], obj.bbox[1])
            elif isinstance(obj, LTTextBoxHorizontal):
                self.set_table_headers(obj, table_headers)

        return table_headers

    def set_cells(self, page, headers, cells=[]):
        Cell = namedtuple("Cell", "col, text, x0, y0")
        col = None
        text = None

        for obj in page:
            if isinstance(obj, LTTextLineHorizontal):
                obj_x0 = obj.bbox[0]
                obj_y0 = obj.bbox[1]

                if obj_y0 < headers['NAME'].y0 and (obj_x0 == headers['NAME'].x0 or
                   obj_x0 == headers['VALUE'].x0 or obj_x0 == headers['REFERENCE RANGE'].x0):

                    if obj_x0 == headers['NAME'].x0:
                        col = 'NAME'
                    elif obj_x0 == headers['VALUE'].x0:
                        col = 'VALUE'
                    elif obj_x0 == headers['REFERENCE RANGE'].x0:
                        col = 'REFERENCE RANGE'

                    text = clean_string(obj.get_text())
                    if text:
                        cells.append(Cell(col, text, obj_x0, obj_y0))

            elif isinstance(obj, LTTextBoxHorizontal):
                self.set_cells(obj, headers, cells)

        return cells

    def set_rows(self, cells):
        Cell = namedtuple("Cell", "col, text, x0, y0")
        Row = namedtuple('Row', 'test, value, ref_range, y0')

        name_col = [cell for cell in cells if cell.col == 'NAME']
        value_col = [cell for cell in cells if cell.col == 'VALUE']
        ref_col = [cell for cell in cells if cell.col == 'REFERENCE RANGE']

        # normalize val col with blank cells to match name col length
        values_y0 = [cell.y0 for cell in value_col]
        missing_val_cells = [cell.y0 for cell in name_col if cell.y0 not in values_y0]
        value_col += [Cell('VALUE', None, None, y) for y in missing_val_cells]

        rows = [Row(name_cell.text, value_cell.text, ref_cell.text, name_cell.y0)
                for name_cell in name_col for value_cell in value_col
                for ref_cell in ref_col
                if name_cell.y0 == value_cell.y0 == ref_cell.y0]

        return rows

    def parse_pages(self):
        pages = self.process_file(self.file_name)
        self.set_metadata(pages[0])

        for page in pages:
            self.table_headers = self.set_table_headers(page)
            self.cells = self.set_cells(page, self.table_headers)

        self.rows = self.set_rows(self.cells)


if __name__ == "__main__":
    file_1 = PdfTable("RawData/pdfs/3768958-2.pdf")
    file_1.parse_pages()

    print("file_1 cells")
    print tabulate(file_1.cells, headers="keys", showindex="always")

    file_2 = PdfTable("RawData/pdfs/3768959.pdf")
    file_2.parse_pages()

    print("\nfile_2 cells")
    print tabulate(file_2.cells, headers="keys", showindex="always")

file_1.cells

    col              text                   x0       y0
--  ---------------  ---------------  --------  -------
 0  NAME             TP                42.8571  570.887
 1  NAME             RIN               42.8571  554.172
 2  VALUE            13.5             221.716   570.887
 3  VALUE            1.0              221.716   554.172
 4  REFERENCE RANGE  11.8-14.2 (SEC)  412.555   570.887
 5  REFERENCE RANGE  0.8-1.2          412.555   554.172

file_2.cells

    col              text                        x0       y0
--  ---------------  --------------------  --------  -------
 0  NAME             TP                     42.8571  570.887
 1  NAME             RIN                    42.8571  554.172
 2  VALUE            13.5                  221.716   570.887
 3  VALUE            1.0                   221.716   554.172
 4  REFERENCE RANGE  11.8-14.2 (SEC)       412.555   570.887
 5  REFERENCE RANGE  0.8-1.2               412.555   554.172
 6  NAME             RSW                    42.8571  570.887
 7  NAME             BCW                    42.8571  554.172
 8  VALUE            8.7                   221.716   570.887
 9  VALUE            25.6                  221.716   554.172
10  REFERENCE RANGE  4.5-12.5              412.555   570.887
11  REFERENCE RANGE  14.0-30.0             412.555   554.172

ожидаемый file_2.cells

    col              text                        x0       y0
--  ---------------  --------------------  --------  -------
 0  NAME             RSW                    42.8571  570.887
 1  NAME             BCW                    42.8571  554.172
 2  VALUE            8.7                   221.716   570.887
 3  VALUE            25.6                  221.716   554.172
 4  REFERENCE RANGE  4.5.-12.5             412.555   570.887
 5  REFERENCE RANGE  14.0-30.0             412.555   554.172

Не только file_1.cells добавляются к file_2.cells, но и после обработки file_2, file_1.cells представляют собой комбинацию ячеек из обоих экземпляров.

1 ответ

Решение

У вас есть изменяемые аргументы по умолчанию table_headers={} а также cells=[] что, вероятно, является проблемой, или, по крайней мере, может вызвать другие проблемы. Эти значения распределяются между вызовами методов, поэтому изменения в одном вызове отражаются в другом месте.

Другие вопросы по тегам