每周问答 这样神奇的代码该怎么理解呢?

匿名 · 2016年07月12日 · 最后由 jacexh 回复于 2016年07月14日 · 2037 次阅读

最近在看 uiautomator 的 Python 封装,代码中使用 param_to_property 的装饰器,就可以实现
d.open.notification(),这样的转换不知该如何理解。
google 了一天了,帮小弟解惑,谢谢啦

def param_to_property(*props, **kwprops):
    if props and kwprops:
        raise SyntaxError("Can not set both props and kwprops at the same time.")

    class Wrapper(object):

        def __init__(self, func):
            self.func = func
            self.kwargs, self.args = {}, []

        def __getattr__(self, attr):
            if kwprops:
                for prop_name, prop_values in kwprops.items():
                    if attr in prop_values and prop_name not in self.kwargs:
                        self.kwargs[prop_name] = attr
                        return self
            elif attr in props:
                self.args.append(attr)
                return self
            raise AttributeError("%s parameter is duplicated or not allowed!" % attr)

        def __call__(self, *args, **kwargs):
            if kwprops:
                kwargs.update(self.kwargs)
                self.kwargs = {}
                return self.func(*args, **kwargs)
            else:
                new_args, self.args = self.args + list(args), []
                return self.func(*new_args, **kwargs)
    return Wrapper
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
...
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
@property    
def open(self):
        '''
        Open notification or quick settings.
        Usage:
        d.open.notification()
        d.open.quick_settings()
        '''
        @param_to_property(action=["notification", "quick_settings"])
        def _open(action):
            if action == "notification":
                return self.server.jsonrpc.openNotification()
            else:
                return self.server.jsonrpc.openQuickSettings()
        return _open
共收到 8 条回复 时间 点赞

少贴点东西吧 def open 上面应该还有东西的

@property
def open(self)
    ...

python 的一些特殊的方法而已

匿名 #2 · 2016年07月12日

#1 楼 @codeskyblue 确实是少了@property,已添加
该怎么理解这个代码呢?(action=["notification", "quick_settings"]) 这个参数是赋给了 param_to_property 的第一层装饰器,第二层装饰器,做了哪些操作呢?谢谢

找本 python 的书翻翻

open 函数返回了一个_open,其中_open 经过 param_to_property 装饰器变成了一个 Wrapper 实例。

在这个 Wrapper 实例被初始化时,构造函数的 func 参数被传递进来了一个原始的_open 函数对象,存储在成员变量 self.func 中。

注 1

本身 param_to_property 是返回一个 Wrapper 类对象的,但是由于 param_to_property 装饰器是以 (action=["notification", "quick_settings"]) 的形式使用的,所以 prop 和 kwprops 被占了,于是_open 函数对象会顺势传递给装饰器【返回的对象】上,怎么传递呢,就是把【返回的对象】调用一下,并且把_open 函数对象作为第一个参数传入,因此 Wrapper 类就被调用了,并且传入了一个_open 对象作为首参数 ——也就是 Wrapper 类被初始化了:Wrapper(_open)

注 2

Wrapper 实例的生命周期范围内可以访问到 props 和 kwprops 这两个变量,kwprops 的值在你的例子中为{"action": ["notification", "quick_settings"]}。

现在 d.open 就成了 Wrapper 实例,因此 d.open.notification 就等于调用了 getattr 且 attr 参数的值为"notification",可以看到代码里返回 self 实例自身。然后 d.open.notification() 又调用了 Wrapper 实例的 call 方法,于是 call 里边有一行代码

return self.func(*args, **kwargs)

调用了被存储的 func,就是原始的_open 函数对象,并且把处理后的 args 和 kwargs 作为参数传入。在例子里,_open 的第一个参数 action 的值为"notification",至于为什么是"notification"请参考 getattr 和 call 里 if kwprops: 分支里的的代码实现

匿名 #4 · 2016年07月12日

#4 楼 @gaudi 非常感谢!基本理解了整个流程。

这段代码的实现我觉得不是特别好

匿名 #7 · 2016年07月13日

#6 楼 @jacexh 可以说说你的想法吗?

#7 楼 @lkx 这段代码的问题在于每次调用 jsonrpc 的 method 都会实例化一个 Wrapper 对象,性能影响很大
把 Wrapper 改成一个 function 比较好

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册