来自 class 实例属性的值被添加到相同 class 的不同实例
Values from a class instance's attribute being added to a different instance of the same class
我正在解析 pdf 以使用我的 PdfTable class 提取 table 数据。当我创建一个 class 实例然后创建另一个 class 实例时,似乎第一个 class 实例 file_1.cells 被添加到第二个 class 实例 [=33] =].我不明白为什么会这样,因为我认为我不是在创建 class 变量,而是在创建实例变量。出于某种原因,当另一个 class 实例被实例化时,来自 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 是来自两个实例的单元格的组合。
您有可变的默认参数 table_headers={}
和 cells=[]
这可能是问题所在,或者至少会导致其他问题。这些值在对方法的调用之间共享,以便一次调用中的更改反映在其他地方。
我正在解析 pdf 以使用我的 PdfTable class 提取 table 数据。当我创建一个 class 实例然后创建另一个 class 实例时,似乎第一个 class 实例 file_1.cells 被添加到第二个 class 实例 [=33] =].我不明白为什么会这样,因为我认为我不是在创建 class 变量,而是在创建实例变量。出于某种原因,当另一个 class 实例被实例化时,来自 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 是来自两个实例的单元格的组合。
您有可变的默认参数 table_headers={}
和 cells=[]
这可能是问题所在,或者至少会导致其他问题。这些值在对方法的调用之间共享,以便一次调用中的更改反映在其他地方。