代码之家  ›  专栏  ›  技术社区  ›  agarrett

Python设计问题(在这种情况下是否可以使用decorator?)

  •  2
  • agarrett  · 技术社区  · 14 年前

    我有一个问题可以简化为:我有一个特定的对象集,我想以特定的方式修改它。所以,我可以编写一个修改单个对象的函数,然后创建一个decorator,将该函数应用于集合中的所有对象。

    那么,假设我有这样的东西:

    def modify_all(f):
        def fun(objs):
            for o in objs:
                f(o)
    
    @modify_all
    def modify(obj):
        # Modify obj in some way.
    
    modify(all_my_objs)
    

    但是,有时我可能只想单独操作一个对象。

    有没有一种方法可以“去装饰”这个房间 modify

    只是为了清楚和完整,有相当多的事情正在进行中 实际的 modify_all 装饰者比在这里说明。两者 修改 全部修改 我有一定的工作要做,这就是为什么我认为装饰师可能很好。另外,我还有其他的 修改 直接受益于 全部修改 ,这使得它更加有用。但有时我确实需要用原版做一些事情 修改 功能。我知道我总是可以传递一个元素集来“诱使”它按原样工作,但我想知道是否有更好的方法或更好的设计来应对这种情况。

    5 回复  |  直到 14 年前
        1
  •  1
  •   aaronasterling    14 年前

    亚历克斯已经发布了一些类似的东西,但这也是我的第一个想法,所以我会继续发布它,因为它回避了麦克格雷厄姆的反对意见(即使我真的不同意他的反对意见:人们对某事一无所知的事实是 不使用它的理由。人们就是这样 持续 不知道)

    def modify_all(f):
        def fun(objs):
            for o in objs:
                f(o)
        fun.unmodified = f
        return fun
    
    def undecorate(f):
        return f.unmodified
    
    @modify_all
    def modify(obj):
        # Modify obj in some way.
    
    modify(all_my_objs)
    undecorate(modify)(one_obj)
    

    你可以打电话给我 undecorate

    另一种选择是这样处理:

    def modify_one(modify, one):
        return modify.unmodified(one)
    
        2
  •  2
  •   habnabit dwc    14 年前

    将decorator手动应用于函数一次,并将结果保存为新名称。

    def modify_one(obj):
        ...
    
    modify_some = modify_all(modify_one)
    modify_some([a, b, c])
    modify_one(d)
    
        3
  •  2
  •   Alex Martelli    14 年前

    修饰符适用于以特定形式应用高阶函数(HOF)的情况

    def f ...
    
    f = HOF(f)
    

    在本例中,语法(具有相同的语义)

    @HOF
    def f ...
    

    f 永远不会用“裸”字。

    对于您的用例,如果您同时需要“baref”和“decoratedf”,那么它们必须有两个不同的名称,因此decorator语法不是立即适用的——但也不是必须的!一个很好的替代方法是使用修饰函数的属性作为修饰名称,例如:

    def modify(obj): ...
    
    modify.all = modify_all(modify)
    

    现在,你可以打电话了 modify(justone) modify.all(allofthem)

        4
  •  0
  •   John La Rooy    14 年前

    你可以用一个装饰师来应用亚历克斯的想法

    >>> def forall(f):
    ...     def fun(objs):
    ...         for o in objs:
    ...             f(o)
    ...     f.forall=fun
    ...     return f
    ... 
    >>> @forall
    ... def modify(obj):
    ...     print "modified ", obj
    ... 
    >>> modify("foobar")
    modified  foobar
    >>> modify.forall("foobar")
    modified  f
    modified  o
    modified  o
    modified  b
    modified  a
    modified  r
    

    也许 foreach 是个更好的名字

        5
  •  -1
  •   David Z    14 年前

    在你的特殊情况下,我会考虑使用参数解包。这只需对现有代码稍作修改即可完成:

    def broadcast(f):
        def fun(*objs):
            for o in objs:
                f(o)
        return fun
    
    @broadcast
    def modify(obj):
        # Modify obj in some way.
    
    modify(*all_my_objs)
    modify(my_obj)
    

    根据您处理的对象类型,另一种选择是让装饰器“智能地”决定是否应该使用循环:

    def opt_broadcast(f):
        def fun(obj):
            if isinstance(obj, list):
                for o in objs:
                    f(o)
            else:
                f(o)
        return fun
    
    @opt_broadcast
    def modify(obj):
        # Modify obj in some way.
    
    modify(all_my_objs)
    modify(my_obj)
    

    不过,当你这么做的时候,可能会产生一些混乱,而且如果你打电话的话,显然是行不通的 modify my_obj 可能是一个列表,不要这样做)