Python 我的装饰师有多糟糕?

Python 我的装饰师有多糟糕?,python,Python,我最近创建了一个@sequenceable decorator,它可以应用于接受一个参数的任何函数,并使它自动适用于任何序列。以下是代码(Python 2.5): 使用中: @sequenceable def unixtime(dt): return int(dt.strftime('%s')) >>> unixtime(datetime.now()) 1291318284 >>> unixtime([datetime.now(), datetime

我最近创建了一个@sequenceable decorator,它可以应用于接受一个参数的任何函数,并使它自动适用于任何序列。以下是代码(Python 2.5):

使用中:

@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}
我的问题是:

  • 这是一个糟糕的想法,为什么
  • 这可能是一个好主意,但在实现时有明显的缺点吗
  • 潜在的陷阱是什么 使用这个代码
  • 有没有一个内置的或图书馆 已经在做我正在做的事了

    • 我不太喜欢尽力帮助打电话的人。Python具有足够的表达能力,因此调用方处理“列表化”并不是什么大问题。调用方很容易写出
      dict
      理解或
      map
      调用

      作为一名Python程序员,这正是我所期望的,因为Python标准库函数并不能帮助我解决这个问题。这个习语实际上让我有点困惑,因为现在我必须试着记住哪些方法是“有用的”,哪些不是

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

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

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

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

      [unixtime(x) for x in [datetime.now(), datetime(2010, 12, 3)]]
      
      这样,您就可以使用适当的Pythonic构造将相同的内容应用于序列,并且不会因为序列的想法而污染
      unixtime
      。您最终会在实现不需要这些知识的地方耦合逻辑(关于排序)

      编辑: 它基本上归结为编码风格、可重用性和可维护性。您需要分区良好的代码,这样在编写
      unixtime
      (比如)时,您只关心转换为
      unixtime
      。然后,如果您对序列感兴趣,您可以设计专门与序列相关的功能(或使用内置语法)。这使得清晰地思考每个操作、测试、调试和重用代码变得更加容易。甚至从名称的角度考虑一下:原始函数被恰当地称为
      unixtime
      ,但是您的序列版本可能更恰当地被称为
      unixtime\u sequence
      ,这很奇怪,并且暗示了一个不寻常的函数

      当然,有时你会违反这条规则。如果(但仅当)性能是一个问题,您可以组合功能。但总的来说,一开始将事物划分为清晰的部分会导致清晰的思维、清晰的编码和易于重用。

      这是一个糟糕的想法。这本质上是松散的打字。鸭子打字就到此为止 我想,东西应该被拿走

      考虑这一点:

      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)})
      
      这样做仍然需要调用方知道(在duck键入精度范围内)传入的内容,并且不会给实际函数增加任何类型检查开销。它也适用于任何类似dict的对象,而您的原始解决方案取决于它是
      dict
      的实际子类,而不是pythonic,因为: -在Python中,显式被认为比隐式更好 -它不像使用内置地图或列表那样是标准的习惯用法

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

      你的装饰师就没用了。您的decorator只能应用于特殊情况,如果您要使用它,您将打破Python Zen的一条规则:“应该有一种——最好只有一种——显而易见的方法来实现它”。

      这也是我的第一反应;但看看布伦特的最后一个例子。他的装饰器在字典中“映射”,将每个键与键值上的函数结果相关联。(我想…)我们有很多实用程序作用于一段数据,用于取消显示字符、删除字符串、转换日期等,最终我们完成了大量的映射、列表理解,以及将dict转换为iterables和iterables,所以我想这可能会减少多余的代码量,只是为了完成这些频繁的任务。@Dan这是正确的。它被设计成经常跨数据结构映射的函数的速记。@Brent Newey:我更喜欢这样的速记:
      somefunc([singleton])
      somefinc(iterable)
      。这样一来,
      somefunc
      总是与iterable一起工作。对我来说似乎更简单。好的,所以我很欣赏这个论点。我很想知道为什么这是件坏事。你能举个例子说明这会把问题搞糊涂还是让我大吃一惊吗?@Brent:我已经补充了一些细节。我想你的意思是让def unixtime()用@pluralize修饰。我尝试了这个例子,它就是这样工作的,在可扩展性方面实现了我想要的,并且明确了我想要做的事情。好节目!我完全同意类型部分,任何采用不相关类型的函数都会被破坏。虽然我根本不会在这里使用装饰器:
      f.on_seq
      显然是
      map
      如果你想让dicts有同样的功能,只需定义一个类似的函数:
      mapvalues=lambda f,d:dict(k,f(v)代表k,v在d.iteritems()中)
      。。它适用于任何没有任何修饰的函数。修饰器仅用于对接受标量值的函数进行操作。但是我理解这个论点。这不是
      map
      的作用吗?不要列出理解,这样做吗?为什么要在已经杂乱无章的概念空间中添加第三种技术?请解释这是如何比
      map
      和列表理解有所改进的。它没有比map()有所改进。这是map()的缩写。我同意map()可能不需要一个,但需要一个sh
      @sequence
      def distance(points):
          return sqrt(reduce(
              lambda a, b: a + b, 
              (a**2 for a in points),
              0))