How terrible is my decorator?

I recently created an @sequenceable decorator that can be applied to any function that takes a single argument and makes it automatically apply to any sequence. This is the code (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 

Using:

 @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} 

My questions:

  • This is a terrible idea and why?
  • Perhaps this is a good idea, but has significant flaws that are implemented?
  • What are the potential pitfalls using this code?
  • Is there a built-in library or is the library already doing what I'm doing?
+4
source share
5 answers

This is a terrible idea. This is essentially an elusive typification. Duck print to such an extent the material should be taken, IMO.

Consider this:

 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 

Then your example will be

 @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)}) 

The execution of this method still requires that the caller knows (up to duck input) what is being transmitted and does not add any type control overhead to the actual function. It will also work with any dictate-like object, whereas your original decision depends on what it is the actual dict subclass of.

+8
source

In my opinion, you seem to be building logic in the wrong place. Why unixtime n't unixtime know anything about sequencing? In some cases, this would be a good idea (for performance or even semantics), but here it seems that you are adding additional functions to unixtime that do not make sense in this context.

It is best to use (say) a list comprehension:

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

That way, you use the correct Pythonic construct to apply the same thing to a sequence, and you don't pollute unixtime with sequence ideas. You finish the logic of communication (about sequence) in those places where the implementation should be free of this knowledge.

EDIT: It basically comes down to the coding style, reusability and maintainability. You want to split the code well, so when you code unixtime (say) you are only concerned with converting to unixtime . Then, if you are interested in sequences, you develop functionality (or use the built-in syntax) that focuses exclusively on sequences. This simplifies reporting of each operation, testing, debugging, and code reuse. Think about it even in terms of a name: the original function is called unixtime , but your version with a sequence might be more conveniently called by unixtime_sequence , which is strange and offers an unusual function.

Sometimes, of course, you break this rule. If (but only when) performance is a problem, you can combine functionality. But in general, dividing things first into clear parts leads to clear thinking, clear coding, and easy reuse.

+2
source

I am not a huge fan of too much to help my subscribers. Python is expressive enough that for a subscriber it doesn’t matter much to process a “list”. It is simple enough so that the caller can record a call to dict or map .

As a Python programmer, what I would expect to do as the functions of the Python standard library do not help me with this. This idiom actually bothers me a bit, because now I have to try to remember which methods are “useful” and which are not.

Being too flexible is a small issue that I am facing with a Python SCons build tool. His methods are very convenient. If you want to install some preprocessors, you can give it a string, a list of strings, tuples, dict, etc. It is very convenient, but a little overwhelming.

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

Not pythonic, because: - in the Python core, it is considered better than implicit - this is not a standard idiom, like using a built-in map or list comprehension.

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

And your decorator becomes useless. Your decorator can only be used for special cases, and if you use it, you will violate one of the rules of Python Zen: "There should be one - and preferably only one - an easy way to do this."

0
source

All Articles