What the heck is an xrange?
late.am
late.am
self._len = int(ceil(float(stop - start) / step))
Python supports arbitrary-length integers; you can't just cast those to (fixed-length) floating point numbers without losing precision. It's better to use integer division here, for example: self._len = (stop - start)//step + bool((stop - start)%step)
(A variant like (stop - start + step - 1)//step works only for positive numbers; I guess it could work if you put it in the earlier if-clause and put a corresponding assignment with +1 at the end in the other branch.)However, I understand if that complaint is just too nitpicky for you to want to mention it. Great post btw.
For example, int(float(10¹⁸ - 1)) != 10¹⁸ - 1, but xrange(1, 10¹⁸) is perfectly valid (even in Python 2).
edit: how do I type two consecutive asterisks on Hacker News? Backslash doesn't seem to work as an escape character.
For example, int(float(10**18 - 1)) != 10**18 - 1,
but xrange(1, 10**18) is perfectly valid (even in Python 2).Edit: failure. That's two stymied people :(
As a side-note, here's a related article on implementing a Python generator "for real" using the C API: http://eli.thegreenplace.net/2012/04/05/implementing-a-gener...
This is an excellent post and every Python hacker should read it. Kudos to the author.
You can do tons of examples, but until you figure out how the machine works inside, you'll be completely lost (you could get it with examples, but you won't necessarily know WHY you got it, so you'll be useless in helping other people learn).
if len(args) == 1:
start, stop, step = 0, args[0], 1
elif len(args) == 2:
start, stop, step = args[0], args[1], 1
elif len(args) == 3:
start, stop, step = args
else:
raise TypeError('xrange() requires 1-3 int arguments')
with: map = [
lambda args: (0, args[0], 1),
lambda args: (args[0], args[1], 1),
lambda args: args,
]
try:
start, stop, step = map[len(args)](args)
except IndexError:
raise TypeError('xrange() requires 1-3 int arguments')
It's more DRY, and it conveys the intent better.I would not do such a change to the if step block since its pattern feels noticeably different: "open" checks fit well in a if/else, whereas bunch-of-equalities fit a dispatch map better (plus you can actually modify the map at runtime).
* Name 'map' for a variable is a poor choice (as it has same name as the python builtin function map)
* Your version has off by one error, it doesn't give correct results when called with a single element list or if the list has three elements:
>>>mymap = [
lambda args: (0, args[0], 1),
lambda args: (args[0], args[1], 1),
lambda args: args,
]
>>>args = [5]
>>>mymap[len(args)](args)
IndexError
Traceback (most recent call last)
....
# This shouldn't be the case, a list with a single
# element is a valid input
>>>args = [1,6,1]
>>>mymap[len(args)](args)
IndexError Traceback (most recent call last)
...
# This shouldn't be the case, a list with a three
# elements is a valid input >>>mymap = [
lambda args: raise IndexError,
lambda args: (0, args[0], 1),
lambda args: (args[0], args[1], 1),
lambda args: args,
]
That aside, this approach would be much slower too.