Created
January 18, 2017 09:52
-
-
Save emmeowzing/8df99fc24230e9146d45b9095125a32e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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