如何扩展工作表 class

How to extend the Worksheet class

您好想在 openpyxl 中扩展工作表 - class。但是,由于这是一个二级容器,我不确定如何。通常在 openpyxl 中,你用 wb = Workbook() 实例化,然后用 ws = wb.activewb.create_sheet() 得到一个 Excel-Worksheet。我在这里对如何进行有点困惑。当然可以:

from openpyxl.worksheet.worksheet import Worksheet
class SheetExtended(Worksheet):
    def adjust_column_width(self):       
        for col in self.columns:
            max_length = 0
            column = col[0].column_letter
            for cell in col:
                if len(str(cell.value)) > max_length:
                    max_length = len(str(cell.value))

            adjusted_width = (max_length + 2) * 1.2
            self.column_dimensions[column].width = adjusted_width

不过我会把它与 Workbook-class?

结合起来

以下是您可以如何进行“猴子修补”Worksheet,所以您所做的是有效的:

from openpyxl import load_workbook
from openpyxl.worksheet.worksheet import Worksheet


def adjust_column_width(self):
    for col in self.columns:
        max_length = 0
        column = col[0].column_letter
        for cell in col:
            if len(str(cell.value)) > max_length:
                max_length = len(str(cell.value))

        adjusted_width = (max_length + 2) * 1.2
        self.column_dimensions[column].width = adjusted_width


# monkey patching the new method onto the existing class
Worksheet.adjust_column_width = adjust_column_width


# an example of using it
wb = load_workbook('wb.xlsx')
wb.active.adjust_column_width()
wb.save('wb.xlsx')

如果您需要 class 方法,您可以这样做:

from types import MethodType


def some_class_method(cls):
    print(cls.__name__)


Worksheet.some_class_method = MethodType(some_class_method, Worksheet)

一般来说,解决问题的方法比猴子修补 classes 更好。 Monkey 补丁添加了 class 用户意想不到的行为,特别是因为它仍然是 'is' 原来的 class,不知道它在现有 class 中的新功能.

您所询问的操作可以很容易地成为一个以 Worksheet 作为参数并对其进行修改的函数,这将是一个更好的解决方案。所以这里真正的问题是:为什么你认为修改 class (或你建议的 subclassing )是更好的解决方案?用例是什么?

谢谢@Grismar 的回答。它把我推向了正确的方向。这个问题的问题是我没有在我的问题中包含 use-case。

我的用例是我想创建一个实用程序 class,我可以多次重复使用它来创建我的 Excel-Files。我有几次生成 Excel-Files 并且每一代的逻辑都不同。

因此,我从 this great post 中了解了 RealPython 中继承和组合之间的区别。我从来没有有意识地做过组合class,但是考虑到当你尝试继承时openpyxl的依赖问题,我认为这是最好的解决方案。

看起来像这样:

class ExcelGeneration:
    def __init__(self, workbook=None, sheetname=None) -> None:
        self.book = workbook if workbook else Workbook()
        self.sheet = self.__set_sheet_name(sheetname)
        
    def __set_sheet_name(self, sheetname):
        if sheetname and sheetname in self.book.worksheets:
            return self.book[sheetname]
        else:
            sheet = self.book.active
            sheet.title = sheetname
            return sheet

这种方法的问题是您现在总是必须使用 self.sheet 而不是 self,但是对于我的用例,我愿意支付那个价格。