我的装饰师有多糟糕?

时间:2010-12-02 19:41:37

标签: python

我最近创建了一个@sequenceable装饰器,它可以应用于任何带有一个参数的函数,并使其自动适用于任何序列。这是代码(Python 2.5):

def sequenceable(func):
    def newfunc(arg):
        if hasattr(arg, '__iter__'):
            if isinstance(arg, dict):
                return dict((k, func(v)) for k, v in arg.iteritems())
            else:
                return map(func, arg)
        else:
            return func(arg)
    return newfunc

使用中:

@sequenceable
def unixtime(dt):
    return int(dt.strftime('%s'))

>>> unixtime(datetime.now())
1291318284
>>> unixtime([datetime.now(), datetime(2010, 12, 3)])
[1291318291, 1291352400]
>>> unixtime({'start': datetime.now(), 'end': datetime(2010, 12, 3)})
{'start': 1291318301, 'end': 1291352400}

我的问题是:

  • 这是一个可怕的想法,为什么?
  • 这可能是一个好主意,但实施后会有明显的缺点吗?
  • 有什么潜在的陷阱 使用此代码?
  • 是否有内置或库 已经做了我正在做的事情?

5 个答案:

答案 0 :(得分:8)

这个 是一个糟糕的主意。这基本上是松散的打字。鸭子打字就是这样 应该采取的东西,IMO。

考虑一下:

def pluralize(f):
    def on_seq(seq):
        return [f(x) for x in seq]
    def on_dict(d):
         return dict((k, f(v)) for k, v in d.iteritems())
    f.on_dict = on_dict
    f.on_seq = on_seq
    return f

然后你的例子变成了

@pluralize
def unixtime(dt):
    return int(dt.strftime('%s'))


unixtime.on_seq([datetime.now(), datetime(2010, 12, 3)])
unixtime.on_dict({'start': datetime.now(), 'end': datetime(2010, 12, 3)})

这样做仍然需要调用者知道(在鸭子类型精确度内)传入的内容并且不会向实际函数添加任何类型检查开销。它也适用于任何类似dict的对象,而原始解决方案依赖于它是dict的实际子类。

答案 1 :(得分:2)

在我看来,你似乎正在把逻辑构建到错误的地方。为什么unixtime应该对排序有所了解?在某些情况下,这是一个好主意(对于性能甚至是语义)但是在这里,你似乎正在为unixtime添加额外的功能,这些功能在这种情况下没有意义。

更好的是使用(比方说)列表理解:

[unixtime(x) for x in [datetime.now(), datetime(2010, 12, 3)]]

这样,您使用适当的Pythonic构造将相同的东西应用于序列,并且您没有使用有关序列的想法来污染unixtime。你最终会在实现应该没有这些知识的地方耦合逻辑(关于排序)。

编辑: 它主要归结为编码风格,可重用性和可维护性。您需要分区良好的代码,因此当您编码unixtime(比如说​​)时,您只关心转换为unixtime。然后,如果您对序列感兴趣,则可以设计专门与序列相关的功能(或使用内置语法)。这使得更容易清楚地思考每个操作,测试,调试和重用代码。即使在名称方面也要考虑它:原始函数被恰当地称为unixtime,但您的序列版本可能更适合称为unixtime_sequence,这很奇怪并且暗示了一个不寻常的函数。

有时,当然,你打破了这条规则。如果(但仅当)性能是一个问题,您可以组合功能。但总的来说,首先将事物划分为明确的部分会导致思路清晰,编码清晰,易于重用。

答案 2 :(得分:1)

我并不是想要过多帮助来电者的忠实粉丝。 Python的表达足以让调用者处理“listification”并不是什么大不了的事。调用者可以轻松地写出dict理解或map来电。

作为一名Python程序员,这是我所期望的,因为Python标准库函数不能帮助我这样做。这个成语实际上让我更加困惑,因为现在我必须设法记住哪些方法是“有用的”,哪些方法不是。

过于灵活是我对基于Python的构建工具SCons的一个小抱怨。它的方法都非常适应。如果你想设置一些预处理器定义你可以给它一个字符串,一个字符串列表,元组,一个字典等等。它非常方便,但有点压倒性。

env = Environment(CPPDEFINES='xyz')                # -Dxyz
env = Environment(CPPDEFINES=[('B', 2), 'A'])      # -DB=2 -DA
env = Environment(CPPDEFINES={'B':2, 'A':None})    # -DA -DB=2

答案 3 :(得分:0)

不是pythonic,因为:   - 在Python中,explicit被认为比隐含更好   - 它不是使用内置地图或列表理解的标准习语

答案 4 :(得分:0)

@sequence
def distance(points):
    return sqrt(reduce(
        lambda a, b: a + b, 
        (a**2 for a in points),
        0))

你的装饰师变得无用。你的装饰者只能用于特殊情况,如果你已经使用它,你将打破Python Zen的一条规则:“应该有一个 - 最好只有一个 - 明显的方法”。