关于python:装饰win32com COM对象的每个方法

Decorate each method of win32com COM object

本问题已经有最佳答案,请猛点这里访问。

Python3我有一个通过win32com.client.dispatch的COM对象,它有许多用于自动化应用程序的方法。我希望每次调用COM对象的任何方法时,python都会根据返回值(实际上是日志记录)做一些事情。我的自动化伪代码如下:

1
2
3
4
5
6
obj = win32com.client.DispatchEx("3rdParty.Application")
obj.methodA(Parameter)
obj.methodB()
obj.non_method_call
obj.methodN()
...

当对obj进行任何方法调用时,我希望此伪代码的目的是什么:

1
2
3
4
5
6
x = obj.any_method(*args)
if x:
    logger.debug(any_method.__name__ + ' was called at ' + datetime.now().strftime("%H:%M") + ' with parameters ' + str(*args)
else:
    logger.error(obj.cerrmsg)
    obj.logout

请注意,any_method.__name__部分很好,但并不重要。python能做到这一点吗,尤其是对于一个COM对象,而不需要编写有限方法集的逻辑?decorator听起来不错,因为它们修改了函数/类,但是我回顾过的文章在这种情况下不起作用(比如通过对象的方法dict),然后我听说它们只在我自己的代码中定义的方法上起作用。@属性已被建议,但我无法解决如何在本例中应用它。欢迎使用高级技巧(getattr、元类、函数工具、包装等),但请演示。


我针对win32com.client测试了我在评论中提到的方法,并遇到了一个基本问题:

self.__getattribute__的递归性意味着试图存储一个对象然后使用self.stored_obj失败,因为它永远递归地调用__getattribute__

如果你原谅使用全局变量来解决这个问题,这意味着你一次只能使用一次,那么它基本上是有效的。

也就是说,调试/跟踪COM对象所发生的情况可能没问题,但这不是一个好的或健壮的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
global _wrapped_object

class LogWrap:
    def __init__(self, object):
        global _wrapped_object
        _wrapped_object = object

    def __getattribute__(self, name):
        global _wrapped_object

        next_hop_attr = _wrapped_object.__getattr__(name)

        if callable(next_hop_attr):

            def logging_attr(*args, **kwargs):
                retval = next_hop_attr(*args, **kwargs)
                print("logging a call to {0} with args -{1}-".format(name, ','.join(args)))
                print("Returned: {0}".format(retval))
                return retval

            return logging_attr

        else:
            return next_hop_attr

    def __setattr__(self, name, value):
        global _wrapped_object
        _wrapped_object.__setattr__(name, value)


from win32com.client import Dispatch
ie = Dispatch('InternetExplorer.Application')
ie = LogWrap(ie)
ie.Visible = True

ie.Navigate2('google.com')

示例输出:

1
2
3
>>> logging a call to Navigate2 with args -google.com-
Returned: None
>>>