subscripting with 2 arguments in python - python

Assume I have a class X which has 2 attributes : i and j.
I want to have :
x = X((1,2,3),(2,3,4)) #this would set i to (1,2,3) and j to (2,3,4)
I now want subscripting to work in the following way :
a, b = x[1,2] #a should now be 2 and b should now be 3
At the moment I'm trying this :
def __getitem__(self, i, j):
return self.x[i] , self.y[j]
However this keeps giving me the error that getitem takes in exactly 3 arguments but 2 is given (when I try to print out x[1,2] for instance)

Comma is the tuple packing operator. x[1, 2] calls x.__getitem__((1, 2)).
def __getitem__(self, ij):
i, j = ij
return self.x[i], self.y[j]

Related

Program based on functions in Python

def add_sub(x,y):
d=x+y
e=x-y
return d,e
result=add_sub(7,6)
print(result)
The output for this function is (13,1), but i need output as mentioned below:
13
1
It needs to be like this:
def add_sub(x,y):
d=x+y
e=x-y
return d,e
result=add_sub(7,6)
for val in result:
print(val)
Since the result is returned on the tuple, tuple prints on that small braces.
Also, you can do this:
def add_sub(x,y):
d=x+y
e=x-y
return d,e
result=add_sub(7,6)
print("\n".join(map(str, result)))
When your function returns an iterable (a tuple in your case), and you know how many elements it will have, you can unpack it very easily:
def add_sub(x, y):
d = x + y
e = x - y
return d, e
result_add, result_sub = add_sub(7,6)
print(result_add)
# 13
print(result_sub)
# 1

python how to make "for-iteration" stop at "len" using dunder methods

I have several Python classes I use for calling C-code, using c-types. The return struct looks something like the below example.
import ctypes
class MyCClass(ctypes.Structure):
_fields_ = [('n_values', ctypes.c_int),\
('values', ctypes.c_double * 5)]
def __repr__(self):
return """n_values : {0}, values : {1}""".format(self.n_values,\
self.values)
def __len__(self):
return self.n_values
def __getitem__(self, key):
return self.values[key]
The values array is fixed size to ease the call to C (using a variable size array here is not an option). The "actual" length of the array is controlled by the n_values variable.
For instance if values is an array of three numbers, say 1, 2 and 3,values=[1, 2, 3, 0, 0] and n_values=3.
This is all fine. The problem is when i implement __len__ and __getitem__.
I want to be able to write code like this
for value in my_class:
#do something
But the iterator does not seem to "get" that the values-array is only n_values long. I.e. it does not seem to use MyCClass.__len__ to halt the iteration. Instead it seems to iterate over the full length of values.
my_class = MyCClass()
my_class.n_values = 3
sample_values = [1, 2, 3]
for i in range(3):
my_class.values[i] = sample_values[i]
i = 0
for value in my_class:
print(i)
i += 1
0
1
2
3
4
I want
i = 0
for value in my_class:
print(i)
i += 1
0
1
2
I know I can code
for i in range(my_class):
# do something with my_class[i]
but that is not what I want.
Does anybody know how to fix this?
With the old-school iterator types, the only way is to raise an IndexError:
def __getitem__(self, key):
if key >= len(self):
raise IndexError
return self.values[key]
For a cleaner solution, consider using the more modern iteration protocol, i.e. returning an iterator instance from an __iter__ method defined on your iterable. That's documented here.

np.vectorize giving me IndexError: invalid index to scalar variable

trying out something simple and it's frustratingly not working:
def myfunc(a,b):
return a+b[0]
v = np.vectorize(myfunc, exclude=['b'])
a = np.array([1,2,3])
b = [0]
v(a,b)
This gives me "IndexError: invalid index to scalar variable."
Upon printing b, it appears that the b taken in by the function is always 0, instead of [0]. Can I specify which arguments should be vectorized and which should remain constant?
When you use excluded=['b'] the keyword parameter b is excluded.
Therefore, you must call v with keyword arguments, e.g. v(a=a, b=b) instead of v(a, b).
If you wish to call v with positional arguments with the second positional argument excluded, then use
v = np.vectorize(myfunc)
v.excluded.add(1)
For example,
import numpy as np
def myfunc(a, b):
return a+b[0]
a = np.array([1,2,3])
b = [0, 1]
v = np.vectorize(myfunc, excluded=['b'])
print(v(a=a, b=b))
# [1 2 3]
v = np.vectorize(myfunc)
v.excluded.add(1)
print(v(a, b))
# [1 2 3]
Well here is the answer:
v.excluded.add(1) works, though passing exclude=['b'] does not, for some reason.
Just add print to see what happens:
def myfunc(a, b):
print(a, b)
return a + b
v = np.vectorize(myfunc)
a = np.array([1,2,3])
b = np.array([0])
v(a, b)
Output:
1 0
1 0
2 0
3 0
The function is applied to all elements of the array. So it receives only scalar values. You cannot index a scalar.

How to use a custom function in max(x, key=custom_function) function?

I have a custom data type, say: mytime, which represent hours and minutes, such as 29:45, it is 29 hours and 45 minutes.
I want to use max built-in function to find the item in a list of lists, whose sum of its elements is the greatest, where all lists contain values of mytime type.
x = [[a, b], [c, d]]
a,b,c,d are of mytime type.
max(x, key=sum)
won't work here, because a,b,c,d, are not integers.
If I type a + b at python command line, I get the sum of these two time values, result is of mytime type, without any errors.
How do I use max function here?
Let's say your class looks like this:
class mytime(object):
def __init__(self, h, m):
self.h = h
self.m = m
def __add__(self, other):
return mytime(self.h + other.h, self.m + other.m)
def __repr__(self):
return '%i:%i' % (self.h, self.m)
and you use it like this:
a = mytime(10, 10)
b = mytime(2, 22)
print a + b
and it will work as expect:
12:32
Problem:
What you want to do is:
l = [a, b]
print sum(l)
but it will fail:
TypeError: unsupported operand type(s) for +: 'int' and 'mytime'
The problem is that the sum function will start with 0 and will add up all values of the list. It will try to evaluate
0 + mytime(10, 10)
which will fail.
Solution:
The solution to your problem is implementing the __radd__ function, which represents "reverse add" and is called when the arguments can't be resolved in the "forward" direction. For example, x + y is evaluated as x.__add__(y) if possible, but if that doesn't exist then Python tries y.__radd__(x).
So you can add the following method to your class:
def __radd__(self, other):
return mytime(self.h, self.m)
and the sum function will work for you (in this implementation ignoring the other value, which is probably fine in your case).
You can write your own sum function:
def my_sum(item):
return sum(60 * e[0] + e[1] for e in item)
x = [[(2,0), (3,0)], [(9, 0), (4, 0)]]
print max(x, key=my_sum)
I have represented your mytime data structure as tuples (with hours and minutes) so you may need to adjust my_sum to your data structure. The only requirement is that the hours and minutes of a mytime can be filled in for e[0] and e[1] respectively.
The above code returns the greatest element (in this case [(9, 0), (4, 0)]).
Are you sure using a + b works? All sum does is repeatedly apply + to adjacent elements (it's the same as reduce(operator.add, sequence) with a special case to break on strings)... So if it does work - then max(x, key=sum) should just work -- as long as mydate supports comparison operators - eg __gt__, __eq__, __lt__
Example
You need to have __gt__ defined for max to work...
class mydate(object):
def __init__(self, num):
self.num = num
def __add__(self, other): # make sure sum works
return self.num + other.num
def __gt__(self, other): # make sure max can do > comparison
return self.num > other.num
def __repr__(self):
return 'date: {}'.format(self.num)
x = mydate(3)
y = mydate(5)
z = mydate(2)
print max([x,y,z], key=sum)

call-by-reference function parameters

Given a function:
def A(a, b, c):
a *= 2
b *= 4
c *= 8
return a+b+c
How can I set the 'c' var to be called-by-reference, so if i call d = A(a,b,c), c will point to the same int object, and be updated from within the function?
You're getting into murky territory: you're talking about declaring global (or nonlocal) variables in functions, which should simply not be done. Functions are meant to do one thing, and do them without the consent of other values or functions affecting their state or output.
It's difficult to suggest a working example: are you alright with having copies of the variables left behind for later reference? You could expand this code to pass back a tuple, and reference the members of the tuple if needed, or the sum:
>>> def A(a, b, c):
return (a*2, b*4, c*8)
>>> d = A(2, 4, 8)
>>> sum(d)
84
>>> d[-1] #or whatever index you'd need...this may serve best as a constant
64
You can do this if c is a list:
c = [2]
def A(a, b, c):
a *= 2
b *= 4
c[0] *= 8
return a+b+c[0]
print c # gives [16]
You can't. Python cannot do that.
What you can do is pass a wrapper that has __imul__() defined and an embedded int, and the augmented assignment will result in the embedded attribute being mutated instead.
All calls in Python are "by reference". Integers are immutable in Python. You can't change them.
class C:
def __init__(self, c):
self.c = c
def __call__(self, a, b):
a *= 2
b *= 4
self.c *= 8
return a + b + self.c
Example
A = C(1)
print A(1, 1), A.c
print A(1, 1), A.c
Output
14 8
70 64

Categories