Python 我的装饰师有多糟糕?
我最近创建了一个@sequenceable decorator,它可以应用于接受一个参数的任何函数,并使它自动适用于任何序列。以下是代码(Python 2.5): 使用中: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
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))