将 __getitem__ 函数添加到 Python 中的 COM 对象,或者:重写 __slots__?
Add __getitem__ function to COM object in Python, OR: Overriding __slots__?
目标
我正在使用 pywin32 和 win32com 从第三方应用程序操作 COM 对象。
我的一个对象是一个列表,我想遍历列表理解中的值。
什么有效
使用某些 COM 对象,我能够成功地执行以下操作:
>>> [obj.Name for obj in myCom.someList if 'Test' in obj.Name]
而且,正如预期的那样,我得到了结果列表:
['Test1', 'Test2', 'Test3']
什么不...
然而,在其他类似的列表对象上,我尝试了同样的事情:
>>> [obj.Name for obj in myCom.anotherList]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "c:\Python27\lib\site-packages\win32com\gen_py\<class_string>.py", line 6575, in __iter__
raise TypeError("This object does not support enumeration")
TypeError: This object does not support enumeration
我试过的
所以我尝试在定义后添加函数 my_iter
:
>>> myCom.anotherList.__iter__ = my_iter
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "c:\Python27\lib\site-packages\win32com\client\__init__.py", line 473, in __setattr__
raise AttributeError("'%s' object has no attribute '%s'" % (repr(self), attr))
AttributeError: '<win32com.gen_py.Thingy instance at 0xDEADBEEF>' object has no attribute '__iter__'
我用 __getitem__
和 next
尝试了同样的事情,但我得到了同样的错误。我读了一些书,我认为这行不通,因为与 __slots__
有关...不确定。
正在检查生成的 Python 来源
在检查了第一个堆栈跟踪中显示的 .py 之后,我查看了 class ISomeList.__iter__
的定义 - 因为它在 post.
的第一个示例中工作得很好
from win32com.client import DispatchBaseClass
LCID = 0x0
class ISomeList(DispatchBaseClass):
def __iter__(self):
"Return a Python iterator for this object"
try:
ob = self._oleobj_.InvokeTypes(-4,LCID,2,(13, 10),())
except pythoncom.error:
raise TypeError("This object does not support enumeration")
return win32com.client.util.Iterator(ob, '{<<<guid>>>}')
并将其与 class IAnotherList.__init__
进行比较:
class IAnotherList(DispatchBaseClass):
def __iter__(self):
"Return a Python iterator for this object"
try:
ob = self._oleobj_.InvokeTypes(-4,LCID,3,(13, 10),())
except pythoncom.error:
raise TypeError("This object does not support enumeration")
return win32com.client.util.Iterator(ob, None)
所以,对我来说,似乎分配给 ob
失败了,然后我们引发了一个错误。
我可以修改生成的源代码以添加 __iter__
方法,但这似乎是一个非常 "works on my machine" 的解决方案。我能做什么?
解决方案
我对任何替代方法都持开放态度,但这是我自己想出的方法。
我发现有一个名为 GetModuleForProgID
的 win32com 辅助函数 - 正如您从名称中所期望的那样,它将使用 COM 程序 ID 和 return 生成的 pywin32 模块来包装那个对象。
密码
最适合我的是:
""" define a method to do my iteration """
def com_generic_iter(self):
current = 1 """ All the com objects I'm working with are 1-based indeces """
while current <= self.Count:
yield self.Item(current)
current += 1
""" I also wanted to support __getitem__ indexing: """
def generic_getitem(self, k):
return self.Item(k)
""" Then dynamically add the method to the application wrapper when I generate my object """
mod = win32com.client.gencache.GetModuleForProgID("MyProgram.Application")
mod.IAnotherList.__iter__ = com_generic_iter
mod.IAnotherList.__getitem__ = generic_getitem
app = win32com.client.Dispatch("MyProgram.Application")
print [x.Name for x in app.AnotherList]
"""
[<win32com.gen_py.MyProgram.IItem instance at 0x12345678>,
<win32com.gen_py.MyProgram.IItem instance at 0x87654321>,
<win32com.gen_py.MyProgram.IDifferentItem instance at 0x99999999>]
"""
print app.AnotherList[1]
"""
<win32com.gen_py.MyProgram.IItem instance at 0x12345678>
"""
编辑:
您还可以像这样用 setattr
添加 __iter__
和 __getitem__
:
mod = win32com.client.gencache.GetModuleForProgID("MyProgram.Application")
IAnotherList = mod.IAnotherList
setattr(IAnotherList, '__getitem__', generic_getitem)
setattr(IAnotherList, '__iter__', com_generic_iter)
来源
我使用 win32com documentation 查找了有关 GetModuleForProgID
的详细信息。
目标
我正在使用 pywin32 和 win32com 从第三方应用程序操作 COM 对象。
我的一个对象是一个列表,我想遍历列表理解中的值。
什么有效
使用某些 COM 对象,我能够成功地执行以下操作:
>>> [obj.Name for obj in myCom.someList if 'Test' in obj.Name]
而且,正如预期的那样,我得到了结果列表:
['Test1', 'Test2', 'Test3']
什么不...
然而,在其他类似的列表对象上,我尝试了同样的事情:
>>> [obj.Name for obj in myCom.anotherList]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "c:\Python27\lib\site-packages\win32com\gen_py\<class_string>.py", line 6575, in __iter__
raise TypeError("This object does not support enumeration")
TypeError: This object does not support enumeration
我试过的
所以我尝试在定义后添加函数 my_iter
:
>>> myCom.anotherList.__iter__ = my_iter
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "c:\Python27\lib\site-packages\win32com\client\__init__.py", line 473, in __setattr__
raise AttributeError("'%s' object has no attribute '%s'" % (repr(self), attr))
AttributeError: '<win32com.gen_py.Thingy instance at 0xDEADBEEF>' object has no attribute '__iter__'
我用 __getitem__
和 next
尝试了同样的事情,但我得到了同样的错误。我读了一些书,我认为这行不通,因为与 __slots__
有关...不确定。
正在检查生成的 Python 来源
在检查了第一个堆栈跟踪中显示的 .py 之后,我查看了 class ISomeList.__iter__
的定义 - 因为它在 post.
from win32com.client import DispatchBaseClass
LCID = 0x0
class ISomeList(DispatchBaseClass):
def __iter__(self):
"Return a Python iterator for this object"
try:
ob = self._oleobj_.InvokeTypes(-4,LCID,2,(13, 10),())
except pythoncom.error:
raise TypeError("This object does not support enumeration")
return win32com.client.util.Iterator(ob, '{<<<guid>>>}')
并将其与 class IAnotherList.__init__
进行比较:
class IAnotherList(DispatchBaseClass):
def __iter__(self):
"Return a Python iterator for this object"
try:
ob = self._oleobj_.InvokeTypes(-4,LCID,3,(13, 10),())
except pythoncom.error:
raise TypeError("This object does not support enumeration")
return win32com.client.util.Iterator(ob, None)
所以,对我来说,似乎分配给 ob
失败了,然后我们引发了一个错误。
我可以修改生成的源代码以添加 __iter__
方法,但这似乎是一个非常 "works on my machine" 的解决方案。我能做什么?
解决方案
我对任何替代方法都持开放态度,但这是我自己想出的方法。
我发现有一个名为 GetModuleForProgID
的 win32com 辅助函数 - 正如您从名称中所期望的那样,它将使用 COM 程序 ID 和 return 生成的 pywin32 模块来包装那个对象。
密码
最适合我的是:
""" define a method to do my iteration """
def com_generic_iter(self):
current = 1 """ All the com objects I'm working with are 1-based indeces """
while current <= self.Count:
yield self.Item(current)
current += 1
""" I also wanted to support __getitem__ indexing: """
def generic_getitem(self, k):
return self.Item(k)
""" Then dynamically add the method to the application wrapper when I generate my object """
mod = win32com.client.gencache.GetModuleForProgID("MyProgram.Application")
mod.IAnotherList.__iter__ = com_generic_iter
mod.IAnotherList.__getitem__ = generic_getitem
app = win32com.client.Dispatch("MyProgram.Application")
print [x.Name for x in app.AnotherList]
"""
[<win32com.gen_py.MyProgram.IItem instance at 0x12345678>,
<win32com.gen_py.MyProgram.IItem instance at 0x87654321>,
<win32com.gen_py.MyProgram.IDifferentItem instance at 0x99999999>]
"""
print app.AnotherList[1]
"""
<win32com.gen_py.MyProgram.IItem instance at 0x12345678>
"""
编辑:
您还可以像这样用 setattr
添加 __iter__
和 __getitem__
:
mod = win32com.client.gencache.GetModuleForProgID("MyProgram.Application")
IAnotherList = mod.IAnotherList
setattr(IAnotherList, '__getitem__', generic_getitem)
setattr(IAnotherList, '__iter__', com_generic_iter)
来源
我使用 win32com documentation 查找了有关 GetModuleForProgID
的详细信息。