【问题标题】:Custom assign operator for Python classPython 类的自定义分配运算符
【发布时间】:2022-01-12 07:38:56
【问题描述】:

我想编写一个行为/看起来类似于 win32com Excel 单元格分配的 Python 类(需要包装它)。我想要有类似的行为,即

ws.Cells(r,c).Value = x   # win32com

myws.setCell(r,c,v)       # "classical" object method in Python

myws(r,c).Value = x       # what I want / need with my own class

myws.Cells(r,c).Value = x # what I want / need with my own class

我发现可以使用__setattr__ 方法,但这不一样,因为我想混合使用myws.x[r][c] = vmyws.setCell(r,c,v) 两种样式。

我什至不知道如何通过__setattr__实现myws.x[r][c] = v

这可能吗?

编辑: 我不想复制 win32com,但我想构建一个看起来与 win32com 相似(不完全相同)的值分配包装器,因为我动态地构建我的 Excel 内容并在最后通过一个调用写入 Excel,当我知道所有的细胞。

编辑2: 这不起作用:

class Cells():

    def __init__(self):
        self.Value = 0

        
class Worksheet(object):

    def __init__(self):        
        self.cells = []
        
    def Cells(self,r,c):
        cell = Cells()
        self.cells.append(cell)
        return cell

ws = Worksheet()

ws.Cells(1,1).Value = 42
print(ws.Cells(1,1).Value)
print(ws.cells)        

【问题讨论】:

  • 为此,您还需要__setitem__(以及相应的__get...__ 方法,可能还有__call__)。你应该先尝试一下这些方法,看看它们是如何工作的。
  • 如果我理解问题描述, __setattr__ 在这里没有用处。您希望 __setitem__ 用于方括号语法,__call__ 用于括号语法(我强烈建议不要这样做)。要使__call__ 方法起作用,您将需要一个代理来表示一个单元格。要使myws[r][c] 工作,您需要使用__getitem__ 接收r 并返回一行的代理,然后让代理的__setitem__ 接受c 并设置单元格值。
  • 在 Excel 对象模型中有 2 种对象类型(类):“工作簿”和“单元格”。所以你可能需要在 Python 中有两个类:Workbook、Cell
  • 我可以举个例子;但由于您询问的是一般技术,因此最好在问题中使用简单、完整的minimal reproducible example。它不需要与电子表格或任何东西交互,但它应该以某种方式对数据的二维存储进行建模(可能使用嵌套列表)。

标签: python class operator-overloading


【解决方案1】:

好的,与同事讨论得出以下解决方案:

class myCell():

    def __init__(self):
        self.Value = 0

class myWorksheet(object):
    """
    wrapper for very fast writing of values to Excel via win32com
    """

    def __init__(self):        
        self.cells = defaultdict(myCell)
        self.minr = float('inf')
        self.maxr = 0
        self.minc = float('inf')
        self.maxc = 0
                
    def Cells(self,r,c):
        self.minr = min(self.minr,r)        
        self.maxr = max(self.maxr,r)    
        self.minc = min(self.minc,c)
        self.maxc = max(self.maxc,c)
        
        return self.cells[(r,c)]
        
    def toArray(self):
        a = [[None]*(self.maxc-self.minc+1) for row in range(self.minr,self.maxr+1)]

        for idx in self.cells.keys():
            r = idx[0]-self.minr
            c = idx[1]-self.minc

            a[r][c] = self.cells[idx].Value

        return a
        
    def __str__(self):
    
        a = self.toArray()
        s = []
        for row in a:
            s.append("["+",".join(["%10s" % c for c in row])+"]")
                
        return "\n".join(s)

    def writeCells(self,ws):
        ws.Range(ws.Cells(self.minr,self.minc),ws.Cells(self.maxr,self.maxc)).Value = self.toArray()
        
ws = Worksheet()

ws.Cells(1,1).Value = 42
ws.Cells(1,2).Value = 43
ws.Cells(1,1).Value = 24
ws.Cells(3,3).Value = "maxCell"
print(ws.Cells(1,1).Value)
print(ws.Cells(1,2).Value)

print(ws)

# if win32com is used, the real worksheet ws can be handed over 
# fws.writeCells(ws)

这会产生以下输出:

24
43
[        24,        43,      None]
[      None,      None,      None]
[      None,      None,   maxCell]

我可以移交给 win32com Range 函数并在 Excel 中一次设置所有数据的最终数组,这比一个接一个地设置每个单元格要快得多,但看起来和感觉就像原始的 win32com 调用。

【讨论】:

    猜你喜欢
    • 2013-08-15
    • 1970-01-01
    • 2018-01-04
    • 1970-01-01
    • 2019-07-28
    • 2016-01-28
    • 2012-07-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多