Skip to content

Instantly share code, notes, and snippets.

@emmeowzing
Created January 18, 2017 09:52
Show Gist options
  • Select an option

  • Save emmeowzing/8df99fc24230e9146d45b9095125a32e to your computer and use it in GitHub Desktop.

Select an option

Save emmeowzing/8df99fc24230e9146d45b9095125a32e to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" Extend the capabilities of _one_ dimensional lists.
More-so an exercise of Python's decorators, magic methods and
OOP.
"""
from warnings import warn
from types import GeneratorType
__author__ = 'Brandon Doyle'
__email__ = '[email protected]'
## Cover the input, allow operalists to be far more flexible than just
## the typical range function, allowing negative and positive intervals
def flylist(f):
def inner(*args, **kw):
if (args.__len__() == 0):
return f(())
elif (args.__len__() > 3):
raise TypeError(\
'operalist expected at most 3 elements, got %d' % len(args))
## Now that we've caught both ends of the bound...
elif (args.__len__() == 1 and \
isinstance(args[0], (list, f, GeneratorType))):
## The following line allows us to convert to operalists.
return f(args[0])
#elif (args.__len__() == 1 and isinstance(args[0], f)):
# return f(args[0])
elif (args.__len__() == 1 and isinstance(args[0], int)):
if (args[0] > 0):
return f(i for i in xrange(args[0]))
elif (args[0] < 0):
return f(i + args[0] + 1 for i in xrange(abs(args[0])))
else:
# Must be 0, just empty f()
return f(())
elif (args.__len__() == 1 and not \
isinstance(args[0], (list, f, GeneratorType))):
raise TypeError(\
'operalist expected int, list or operalist, got %s' \
% type(args[0]))
## else (len(args) == 2 or len(args) == 3)
# the positive interval, default step case
if (args[0] < args[1] and args.__len__() == 2):
# default step 1
return f(args[0] + i for i in xrange(args[1] - args[0]))
# the negative interval, default step case
elif (args[0] > args[1] and args.__len__() == 2):
# default step -1
return f(args[0] - i for i in xrange(args[0] - args[1]))
# the positive interval, (possibly) non-default step case
elif (args[0] < args[1] and args.__len__() == 3):
if (args[2] == 0):
# different error message than if we have a (-) step
raise ValueError(\
'operalist() step argument must not be zero')
if (args[2] <= -1):
# different error than 0 step case
raise ValueError(\
'operalist() (+) interval must have (+) step argument')
else:
# args[2] >= 1
return f(args[0] + i * args[2] \
for i in xrange((args[1] - args[0]) // args[2]))
elif (args[0] > args[1] and args.__len__() == 3):
# negative interval
if (args[2] == 0):
# again, different error message than a (+) interval
raise ValueError(\
'operalist() step argument must not be zero')
if (args[2] >= 1):
# again again, different error than 0 step case
raise ValueError(\
'operalist() (-) interval must have (-) step argument')
else:
# args[2] <= -1
return f(args[0] + i * args[2] \
for i in xrange((args[1] - args[0]) // args[2]))
return inner
@flylist
class operalist(list):
""" A one dimensional numerical iterable with useful attributes from
lists, integers and floats.
Example usage:
>>> from operalist import operalist
>>> operalist(1, -7, -2) // -5
[-1, 0, 0, 1]
>>> operalist(1, -7, -2) ** 2
[1, 1, 9, 25]
>>> operalist(1, -7, -2)
[1, -1, -3, -5]
>>> operalist(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> operalist(1, 11)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> -operalist(1, 11)
[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10]
>>> (-1) * operalist(1, 11)
[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10]
>>> operalist(1, 11) * (-1)
[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10]
>>> operalist(1, 11) ** 2
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> operalist(1, 11) ** -2
[1.0, 0.25, 0.1111111111111111, 0.0625, 0.04, 0.027777777777777776,
0.02040816326530612, 0.015625, 0.012345679012345678, 0.01]
>>> operalist(1, 11, 2)
[1, 3, 5, 7, 9]
>>> operalist(-15, 1)
[-15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0]
>>> operalist(1, -25, -2)
[1, -1, -3, -5, -7, -9, -11, -13, -15, -17, -19, -21, -23]
The idea behind making an operalist capable of creating negative
intervals, or capable of `backward` intervals, is that there have
been times when I wish I could iterate over such numbers in lists,
such as
>>> [-9 + i for i in xrange(10)] # won't do negative intervals
[-9, -8, -7, -6, -5, -4, -3, -2, -1, 0]
Instead we could just type
>>> operalist(-10)
[-9, -8, -7, -6, -5, -4, -3, -2, -1, 0]
"""
def __floordiv__(self, other):
if (isinstance(other, (int, float))):
return operalist(i // other for i in self)
elif (isinstance(other, (list, operalist))):
if (other.__len__() != self.__len__()):
warn('len(other) != len(self): Possible data loss')
return operalist(i // j for i, j in zip(self, other))
else:
raise TypeError
def __div__(self, other):
if (isinstance(other, (int, float))):
return operalist(i / other for i in self)
elif (isinstance(other, (list, operalist))):
if (other.__len__() != self.__len__()):
warn('len(other) != len(self): Possible data loss')
return operalist(i / j for i, j in zip(self, other))
else:
raise TypeError
def __mul__(self, other):
if (isinstance(other, int)):
return operalist(i * other for i in self)
elif (isinstance(other, (list, operalist))):
# multiply entry-wise
if (other.__len__() != self.__len__()):
warn('len(other) != len(self): Possible data loss')
return operalist(i * j for i, j in zip(self, other))
else:
raise TypeError
def __pow__(self, other):
if (isinstance(other, (int, float))):
return operalist(i ** other for i in self)
elif (isinstance(other, (list, operalist))):
if (other.__len__() != self.__len__()):
warn('len(other) != len(self): Possible data loss')
return operalist(i ** j for i, j in zip(self, other))
else:
raise TypeError
def __add__(self, other):
if (isinstance(other, (int, float))):
return operalist(i + other for i in self)
elif (isinstance(other, (list, operalist))):
if (other.__len__() != self.__len__()):
warn('len(other) != len(self): Possible data loss')
return operalist(i + j for i, j in zip(self, other))
else:
raise TypeError
def __sub__(self, other):
return self.__add__(-other)
def __rsub__(self, other):
def __le__(self, other):
if (isinstance(other, (int, float))):
return all(operalist(i <= other for i in self))
elif (isinstance(other, (list, operalist))):
if (other.__len__() != self.__len__()):
warn('len(other) != len(self): Possible data loss')
return all(operalist(i <= j for i, j in zip(self, other)))
else:
raise TypeError
def __ge__(self, other):
if (isinstance(other, (int, float))):
return all(operalist(i >= other for i in self))
elif (isinstance(other, (list, operalist))):
if (other.__len__() != self.__len__()):
warn('len(other) != len(self): Possible data loss')
return all(operalist(i >= j for i, j in zip(self, other)))
else:
raise TypeError
def __eq__(self, other):
if (isinstance(other, (int, float))):
return all(operalist(i == other for i in self))
elif (isinstance(other, (list, operalist))):
if (other.__len__() != self.__len__()):
warn('len(other) != len(self): Possible data loss')
return all(operalist(i == j for i, j in zip(self, other)))
else:
raise TypeError
def __ne__(self, other):
return not self.__eq__(other)
def __lt__(self, other):
return not self.__ge__(other)
def __gt__(self, other):
return not self.__le__(other)
def __mod__(self, other):
if (isinstance(other, (int, float))):
return operalist(i % other for i in self)
elif (isinstance(other, (list, operalist))):
if (other.__len__() != self.__len__()):
warn('len(other) != len(self): Possible data loss')
return operalist(i % j for i, j in zip(self, other))
else:
raise TypeError
def __divmod__(self, other):
if (isinstance(other, (int, float))):
return operalist((i / other, i % other) for i in self)
elif (isinstance(other, (list, operalist))):
if (other.__len__() != self.__len__()):
warn('len(other) != len(self): Possible data loss')
return operalist(operalist([i / j, i % j]) \
for i, j in zip(self, other))
else:
raise TypeError
def __neg__(self):
return self.__rmul__(-1)
def __pos__(self):
return self.__rmul__(+1)
def __rmul__(self, other):
# Just the opposite of l-__mul__
if (isinstance(other, (int, float))):
return operalist(other * i for i in self)
elif (isinstance(other, (list, operalist))):
if (other.__len__() != self.__len__()):
warn('len(other) != len(self): Possible data loss')
return operalist(i * j for i, j in zip(other, self))
else:
raise TypeError
def __iadd__(self, other):
pass
def xoperalist(*args):
""" Just a more flexible generator than xrange(). operalist() basically
covers range(), I'd just like to create a generator-like iterable that's
parallel to xrange() with the same flexibility as operalist(). """
for i in args:
if not (isinstance(i, int)):
raise TypeError(\
'xoperalist() integer end argument expected, got %s' \
% type(args[0]))
else:
# Not a problem with types
if (args.__len__() > 3):
raise TypeError(\
'xoperalist expected at most 3 elements, got %d' % args.__len__())
elif (args.__len__() == 0):
raise TypeError(\
'xoperalist expected at least 1 arguments, got' % args.__len__())
## Totally separate, handle each case separately, as in @flylist
if (args.__len__() == 1):
if not (args[0]):
pass
elif (args[0] > 0):
## a generator from (args[0] - 1) -> 0
iteration = 0
while (iteration < args[0]):
yield iteration
iteration += 1
elif (args[0] < 0):
## a generator from (args[0] + 1) -> 0
iteration = args[0] + 1
while (iteration <= 0):
yield iteration
iteration += 1
elif (args.__len__() == 2):
if (args[0] < args[1]):
## Positive interval, unit step
iteration = 0
while (iteration < (args[1] - args[0])):
yield args[0] + iteration
iteration += 1
elif (args[0] > args[1]):
## Negative interval, unit negative step
iteration = 0
while (iteration < (args[0] - args[1])):
yield args[0] - iteration
iteration += 1
'''
else:
## args[0] == args[1]
pass
'''
elif (args.__len__() == 3):
## same as before, just a different step
if (args[2] == 0):
raise ValueError('xoperalist() step argument must not be zero')
if (args[0] < args[1]):
## Positive interval, possible non-unit step
if (args[2] < 0):
raise ValueError(\
'xoperalist() (+) interval must have (+) step argument')
iteration = 0
while (iteration < (args[1] - args[0]) // args[2]):
yield args[0] + args[2] * iteration
iteration += 1
elif (args[1] < args[0]):
## Negative interval, again possible non-negative unit step
if (args[2] > 0):
raise ValueError(\
'xoperalist() (-) interval must have (-) step argument')
iteration = 0
while (iteration < abs((args[1] - args[0]) // args[2])):
yield args[0] - abs(args[2] * iteration)
iteration += 1
'''
else:
## again, args[0] == args[1]
pass
'''
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment