Python list operations with elements - python

I was trying to operate with a list and a loop. The thing is that I have a list like the following a = [9, 3, 5, 2] and I want to subtract 1 to each element... So I have tried something like this
a = [9, 3, 5, 2]
b = -1
x = a - b

Somewhat beyond the scope of your actual question but you could use some magic functions to abstract away the details:
class MyCoolList(list):
def __sub__(self, other):
return [item - other for item in self]
def __add__(self, other):
return [item + other for item in self]
def __mul__(self, other):
return [item * other for item in self]
Now we can do:
cls = MyCoolList([9, 3, 5, 2])
print(cls - 1)
print(cls + 1)
print(cls * 2)
Which yields
[8, 2, 4, 1]
[10, 4, 6, 3]
[18, 6, 10, 4]
To not repeat yourself (DRY), you may very well use the operator module:
import operator as op
class MyCoolList(list):
def calc(self, what, other):
return [what(item, other) for item in self]
def __sub__(self, other):
return self.calc(op.sub, other)
def __add__(self, other):
return self.calc(op.add, other)
def __mul__(self, other):
return self.calc(op.mul, other)
In the end, you could use a decorator altogether:
import operator as op
def calc(operator_function):
def real_decorator(function):
def wrapper(*args, **kwargs):
lst, other = args
return [operator_function(item, other) for item in lst]
return wrapper
return real_decorator
class MyCoolList(list):
#calc(op.sub)
def __sub__(self, other):
pass
#calc(op.add)
def __add__(self, other):
pass
#calc(op.mul)
def __mul__(self, other):
pass
cls = MyCoolList([9, 3, 5, 2])
print(cls - 1)
print(cls + 1)

use list comprehension
a = [9, 3, 5, 2]
b = [x-1 for x in a]
output:
[8, 2, 4, 1]

Simple one liner using lambda and map
a = [9, 3, 5, 2]
x = list(map(lambda i: i-1, a))
print(x)

If you are new to python here is the simplest one
a = [9, 3, 5, 2]
b=[]
for i in a:
b.append(i-1)
print(b)
OUTPUT
[8, 2, 4, 1]

Related

Adding recursive copy method to linked list implementation in Python?

Hi I implemented a linked list in Python and I got this piece of code that can copy the linked list to another linked list but I cannot understand why its inserting at 0 index instead?
Then wouldn't the copied list be reversed? But I tried running it and the output is correct and is in order?
My insert function
def insert(self, index, item):
if index<0 and index<-1*len(self):
raise IndexError("Negative index out of range")
elif index>0 and index>=len(self):
raise IndexError("Positive index out of range")
if index==0:
self.head=Node(item,self.head)
elif index>0:
node=self._get_node(index-1)
node.next=Node(item,node.next)
else:
node=self._get_node(index+len(self))
node.next=Node(item,node.next)
self.count+=1
My copy function
def copy(self):
new_list=linkedList()
self._copy_aux_(self.head,new_list)
return new_list
def _copy_aux_(self, node, new_list):
if node is not None:
self._copy_aux_(node.next, new_list)
new_list.insert(0,node.item)
Can someone please help explain this?Any help will be appreciated thanks!
EDIT:Okay apparently it inserts the last item first?Why is that?
1 -When you call insert with index equal to zero you insert the list item upfront, that is, in the location pointed by head(see method def insert(self, index, item):
2 - When the method copy is called the method def _copy_aux_(self, node, new_list): is called recursively until the last item of the list is reached, which has node.next equals to None.
3 - After that, after each return from the method _copy_aux_, it starts inserting the itens in the new_list up front from the last to the first item, which gives the correct order.
I suggest to include prints to trace the list copy recursion, as the code below:
def copy(self):
print('Trace copy:')
new_list=linkedList()
self._copy_aux_(self.head,new_list)
return new_list
def _copy_aux(self, node, new_list):
if node is not None:
self._copy_aux_(node.next, new_list)
new_list.insert(0,node.item)
print(new_list)
then run the following example (taken from https://repl.it/#MuhammadFermi/week8-2):
list = linkedList()
list.insert(0, 3)
list.insert(0, 9)
list.insert(0, 2)
list.insert(0, 5)
list.insert(0, 1)
list.insert(2, 6)
list.insert(10, 7)
print(list)
print(len(list))
print(3 in list)
list2 = list.copy()
print('list2 =')
print(list2)
You should get the following results:
1, 5, 6, 2, 9, 3, 7,
7
True
Trace copy:
7,
3, 7,
9, 3, 7,
2, 9, 3, 7,
6, 2, 9, 3, 7,
5, 6, 2, 9, 3, 7,
1, 5, 6, 2, 9, 3, 7,
list2 =
1, 5, 6, 2, 9, 3, 7,
Process finished with exit code 0
I suggest flattening your list, and then re-inserting the values into a new list:
class LinkedList:
def __init__(self, value = None):
self.head = value
self._next = None
def insert_node(self, _val):
if self.head is None:
self.head = _val
else:
getattr(self._next, 'insert_node', lambda x:setattr(self, '_next', LinkedList(x)))(_val)
def flatten(self):
return [self.head, *getattr(self._next, 'flatten', lambda :[])()]
#classmethod
def copy(cls, l):
t = cls()
for i in l.flatten():
t.insert_node(i)
return t
l = LinkedList()
for i in range(10):
l.insert_node(i)
new_l = LinkedList.copy(l)

Pythonic way to add a list of vectors

I am trying to create a method (sum) that takes a variable number of vectors and adds them in. For educational purposes, I have written my own Vector class, and the underlying data is stored in an instance variable named data.
My code for the #classmethod sum works (for each of the vectors passed in, loop through each element in the data variable and add it to a result list), but it seems non-Pythonic, and wondering if there is a better way?
class Vector(object):
def __init__(self, data):
self.data = data
#classmethod
def sum(cls, *args):
result = [0 for _ in range(len(args[0].data))]
for v in args:
if len(v.data) != len(result): raise
for i, element in enumerate(v.data):
result[i] += element
return cls(result)
itertools.izip_longest may come very handy in your situation:
a = [1, 2, 3, 4]
b = [1, 2, 3, 4, 5, 6]
c = [1, 2]
lists = (a, b, c)
result = [sum(el) for el in itertools.izip_longest(*lists, fillvalue=0)]
And here you got what you wanted:
>>> result
[3, 6, 6, 8, 5, 6]
What it does is simply zips up your lists together, by filling empty value with 0. e.g. izip_longest(a, b) would be [(1, 1), (2, 2), (3, 0), (4, 0)]. Then just sums up all the values in each tuple element of the intermediate list.
So here you go step by step:
>>> lists
([1, 2, 3, 4], [1, 2, 3, 4, 5, 6], [1, 2])
>>> list(itertools.izip_longest(*lists, fillvalue=0))
[(1, 1, 1), (2, 2, 2), (3, 3, 0), (4, 4, 0), (0, 5, 0), (0, 6, 0)]
So if you run a list comprehension, summing up all sub-elements, you get your result.
Another thing that you could do (and that might be more "pythonic") would be to implement the __add__ magic method, so you can use + and sum directly on vectors.
class Vector(object):
def __init__(self, data):
self.data = data
def __add__(self, other):
if isinstance(other, Vector):
return Vector([s + o for s, o in zip(self.data, other.data)])
if isinstance(other, int):
return Vector([s + other for s in self.data])
raise TypeError("can not add %s to vector" % other)
def __radd__(self, other):
return self.__add__(other)
def __repr__(self):
return "Vector(%r)" % self.data
Here, I also implemented addition of Vector and int, adding the number on each of the Vector's data elements, and the "reverse addition" __radd__, to make sum work properly.
Example:
>>> v1 = Vector([1,2,3])
>>> v2 = Vector([4,5,6])
>>> v3 = Vector([7,8,9])
>>> v1 + v2 + v3
Vector([12, 15, 18])
>>> sum([v1,v2,v3])
Vector([12, 15, 18])
args = [[1, 2, 3],
[10, 20, 30],
[7, 3, 15]]
result = [sum(data) for data in zip(*args)]
# [18, 25, 48]
Is this what you want?

How to multiply functions in python?

def sub3(n):
return n - 3
def square(n):
return n * n
It's easy to compose functions in Python:
>>> my_list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> [square(sub3(n)) for n in my_list]
[9, 4, 1, 0, 1, 4, 9, 16, 25, 36]
Unfortunately, to use the composition as a key it's awkward, you have to use them in another function which calls both functions in turn:
>>> sorted(my_list, key=lambda n: square(sub3(n)))
[3, 2, 4, 1, 5, 0, 6, 7, 8, 9]
This should really just be sorted(my_list, key=square*sub3), because heck, function __mul__ isn't used for anything else anyway:
>>> square * sub3
TypeError: unsupported operand type(s) for *: 'function' and 'function'
Well let's just define it then!
>>> type(sub3).__mul__ = 'something'
TypeError: can't set attributes of built-in/extension type 'function'
D'oh!
>>> class ComposableFunction(types.FunctionType):
... pass
...
TypeError: Error when calling the metaclass bases
type 'function' is not an acceptable base type
D'oh!
class Hack(object):
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
return self.function(*args, **kwargs)
def __mul__(self, other):
def hack(*args, **kwargs):
return self.function(other(*args, **kwargs))
return Hack(hack)
Hey, now we're getting somewhere..
>>> square = Hack(square)
>>> sub3 = Hack(sub3)
>>> [square(sub3(n)) for n in my_list]
[9, 4, 1, 0, 1, 4, 9, 16, 25, 36]
>>> [(square*sub3)(n) for n in my_list]
[9, 4, 1, 0, 1, 4, 9, 16, 25, 36]
>>> sorted(my_list, key=square*sub3)
[3, 2, 4, 1, 5, 0, 6, 7, 8, 9]
But I don't want a Hack callable class! The scoping rules are different in ways I don't fully understand, and it's arguably even uglier than just using the "lameda". Is it possible to get composition working directly with functions somehow?
You can use your hack class as a decorator pretty much as it's written, though you'd likely want to choose a more appropriate name for the class.
Like this:
class Composable(object):
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
return self.function(*args, **kwargs)
def __mul__(self, other):
#Composable
def composed(*args, **kwargs):
return self.function(other(*args, **kwargs))
return composed
def __rmul__(self, other):
#Composable
def composed(*args, **kwargs):
return other(self.function(*args, **kwargs))
return composed
You can then decorate your functions like so:
#Composable
def sub3(n):
return n - 3
#Composable
def square(n):
return n * n
And compose them like so:
(square * sub3)(n)
Basically it's the same thing you've accomplished using your hack class, but using it as a decorator.
Python does not (and likely will never) have support for function composition either at the syntactic level or as a standard library function. There are various 3rd party modules (such as functional) that provide a higher-order function that implements function composition.
Maybe something like this:
class Composition(object):
def __init__(self, *args):
self.functions = args
def __call__(self, arg):
result = arg
for f in reversed(self.functions):
result = f(result)
return result
And then:
sorted(my_list, key=Composition(square, sub3))
You can compose functions using SSPipe library:
from sspipe import p, px
sub3 = px - 3
square = px * px
composed = sub3 | square
print(5 | composed)

Python yield a list with generator

I was getting confused by the purpose of "return" and "yield"
def countMoreThanOne():
return (yy for yy in xrange(1,10,2))
def countMoreThanOne():
yield (yy for yy in xrange(1,10,2))
What is the difference on the above function?
Is it impossible to access the content inside the function using yield?
In first you return a generator
from itertools import chain
def countMoreThanOne():
return (yy for yy in xrange(1,10,2))
print list(countMoreThanOne())
>>>
[1, 3, 5, 7, 9]
while in this you are yielding a generator so that a generator within the generator
def countMoreThanOne():
yield (yy for yy in xrange(1,10,2))
print list(countMoreThanOne())
print list(chain.from_iterable(countMoreThanOne()))
[<generator object <genexpr> at 0x7f0fd85c8f00>]
[1, 3, 5, 7, 9]
if you use list comprehension then difference can be clearly seen:-
in first:-
def countMoreThanOne():
return [yy for yy in xrange(1,10,2)]
print countMoreThanOne()
>>>
[1, 3, 5, 7, 9]
def countMoreThanOne1():
yield [yy for yy in xrange(1,10,2)]
print countMoreThanOne1()
<generator object countMoreThanOne1 at 0x7fca33f70eb0>
>>>
After reading your other comments I think you should write the function like this:
def countMoreThanOne():
return xrange(1, 10, 2)
>>> print countMoreThanOne()
xrange(1, 11, 2)
>>> print list(countMoreThanOne())
[1, 3, 5, 7, 9]
or even better, to have some point in making it a function:
def oddNumbersLessThan(stop):
return xrange(1, stop, 2)
>>> print list(oddNumbersLessThan(15))
[1, 3, 5, 7, 9, 11, 13]

Setting a subsection or slice of a global numpy array through a python object

I am trying to reference a slice of a "global" numpy array via a object attribute. Here is what I think the class structure would be like and it's use case.
import numpy
class X:
def __init__(self, parent):
self.parent = parent
self.pid = [0, 1, 2]
def __getattr__(self, name):
if name == 'values':
return self.parent.P[self.pid]
else:
raise AttributeError
class Node:
def __init__(self):
self.P = numpy.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
self._values = X(self)
def __getattr__(self, name):
if name == 'x':
return self._values.values
else:
raise AttributeError
Here is the use case:
>>> n = Node()
>>> print n.P
[ 1 2 3 4 5 6 7 8 9 10]
>>> print n.x
[1 2 3]
>>> print n.x[1:3]
[2 3]
Which works fine, now I would like to assign values to n.P through the n.x attribute by,
>>> n.x = numpy.array([11, 12, 13])
to get,
>>> print n.P
[ 11 12 13 4 5 6 7 8 9 10]
Or assign values to slices by,
>>> n.x[1:3] = numpy.array([77, 88])
to get,
>>> print n.P
[ 11 77 88 4 5 6 7 8 9 10]
But for the life of me, I'm struggling to get this assignment working. I thought it would be easy using __setattr__ and __setitem__, but a whole day later I still haven't managed it.
Ultimately, n.x will be returned as a multi-dimensional array where the X class will reshape is on return, but is stored in a n.P which is a vector. I have removed this to simplify the problem.
I would love some help on this. Has anyone done this before? Or suggest how to do this?
Thanks in advance for your help.
SOLUTION
So after many days of stumbling around I found a solution. I suspect this can be simplified and refined. The solution is to create a X object in your Node object. When it's retrieved, it returns temporary numpy object (Values) with knowledge of it's parent node and pids. The setslice_ function is defined in this update the global P array with the new values. If the X object is is assigned, it doesn't return a Values object but sets the global P values directly.
Two points, which may be invalid: 1. The Node and X objects had to be a sub-class of object; 2. if setting a higher dimension array, you need to use __setitem__ instead, which won't work on 1D arrays or lists.
As I said I suspect this code can be improves since I'm not sure I fully understand it. I am happy to take improvements and suggestions.
Thanks for your help, especially Bago.
Here is my final code.
import numpy
class Values(numpy.ndarray):
def __new__(cls, input_array, node, pids):
obj = numpy.asarray(input_array).view(cls)
obj.node = node
obj.pids = pids
return obj
def __setslice__(self, i, j, values):
self.node._set_values(self.pids[i:j], values)
class X(object):
def __get__(self, instance, owner):
p = instance.P[instance.pids]
return Values(p, instance, instance.pids)
def __set__(self, instance, values):
instance.P[instance.pids] = values
class Node(object):
x = X()
def __init__(self, pids=[0, 1, 2]):
self.P = numpy.arange(11)
self.pids = pids
def _set_values(self, pids, values):
self.P[pids] = values
node = Node(pids=[4, 5, 6, 7])
print '\nInitial State:'
print 'P =', node.P
print 'x =', node.x
print 'x[1:3] =', node.x[1:3]
print '\nSetting node.x = [44, 55, 66, 77]:'
node.x = [44, 55, 66, 77]
print 'P =', node.P
print 'x =', node.x
print 'x[1:3] =', node.x[1:3]
print '\nSetting node.x[1:3] = [100, 200]:'
node.x[1:3] = [100, 200]
print 'P =', node.P
print 'x =', node.x
print 'x[1:3] =', node.x[1:3]
It's not clear to me what's not working, but I think maybe you're trying to do something like this:
import numpy
class X(object):
def __init__(self, parent):
self.parent = parent
self.pid = [0, 1, 2]
#property
def values(self):
tmp = self.parent.P[self.pid]
return tmp
#values.setter
def values(self, input):
self.parent.P[self.pid] = input
class Node(object):
def __init__(self):
self.P = numpy.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
self._values = X(self)
#property
def x(self):
return self._values.values
#x.setter
def x(self, input):
self._values.values = input
I hope that get's you started.
update
The reason that n.x[1:3] = [77, 88] doesn't work using this approach is because n.x and n.x[:] = ~ both call the get method of X.values which returns tmp. But tmp is a copy of part of P and after n.x[:] = ~ tmp is thrown away and P is not updated. tmp is a copy because when you index an array with another array you get a copy not a view. Here is an example to make that more clear, you can read more about numpy slicing/indexing here.
>>> P = np.arange(10)
>>> pid = np.array([1, 2, 3])
>>> Q = P[pid]
>>> Q[:] = 99
>>> P
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> R = P[1:4]
>>> R[:] = 99
>>> P
array([ 0, 99, 99, 99, 4, 5, 6, 7, 8, 9])
>>> P[[1,2]][:] = 88
>>> P
array([ 0, 99, 99, 99, 4, 5, 6, 7, 8, 9])
setitem won't help, because you're calling the setitem method of the tmp not X.
The easiest way to make it work is to replace the pid array with a slice, but I know that's kind of limiting. You could also keep track of the tmp array, have a self._tmp so you can move the values from _tmp to P later. I know neither of those are perfect, but maybe someone else here will come up with a better approach. Sorry I couldn't do more.

Categories

Resources