Python - is there a way to implement __getitem__ for multidimension array? - python

I would like use something like that:
class Board():
...
def __getitem__(self, y, x):
return self.board[y][x]
but unfortunatelly, when I call:
board[x][y]
I get:
TypeError: __getitem__() takes exactly 3 arguments (2 given)

When you do board[x][y] you will cause two calls to __getitem__ because you are doing two separate accesses: [x] is one and [y] is another. There's no way to handle this directly in __getitem__; you'd have to have board[x] return some kind of sub-object that you could use [y] on to get the individual item. What you probably want is to have __getitem__ accept a tuple:
def __getitem__(self, tup):
y, x = tup
return self.board[y][x]
Then do:
board[x, y]
(Note that you have the order of x and y switched between __getitem__ and board[x][y] --- is that intentional?)

You might want to consider using this syntax:
board[(x, y)]
It's less pretty, but it allows you to have multidimensional arrays simply. Any number of dimensions in fact:
board[(1,6,34,2,6)]
By making board a defaultdict you can even have sparse dictionaries:
board[(1,6,34,2,6)]
>>> from collections import defaultdict
>>> board = defaultdict(lambda: 0)
>>> board[(1,6,8)] = 7
>>> board[(1,6,8)]
7
>>> board[(5,6,3)]
0
If you want something more advanced than that you probably want NumPy.

board[x][y] means board.__getitem__(x).__getitem__(y), so Board.__getitem__ has to return some kind of view that also supports __getitem__ and remembers x. This is a bit of work, but for some use cases (anything involving passing that view around) it's very convenient.
Another option is board[x, y], which means board.__getitem__((x, y)). Note that this passes a tuple to __getitem__, which you'll have to unpack manually (there is syntactic sugar for doing this in 2.x, but it's a bit obscure and also gone in 3.x, so you may want to avoid it in the interest of future porting work).

Just do:
class Board():
def __getitem__(self, x):
return self.board[x]
because when you call b[x][y] it actually calls __getitem__() twice, as showed below:
import numpy as np
b = Board()
b.board = np.random.random((3,3,3))
print (b[2][0]==(b[2])[0]).all()
#True
But the best would be to subclass np.ndarray, so that you don't have to re-implement this method:
class Board(np.ndarray):
pass

Say b is the class object b = Board(). When you are looking for B[0][0] __getitem__ won't normally work. Instead what we can do is set b's data equal to a new variable.
boardData = b.data
print(boardData[0][0])

Related

Default parameter value for objects

Python provides a way to set a default value for function parameters. An example is:
def f(x=3):
print(x)
This is for a primitive type, lets try with objects:
def f(x=list()):
print(id(x))
f()
44289920
f()
44289920
Same object! I was surprised of this being used to the C/C++ way. Done with that, I now understand the default value is not build at invoking time but at definition time.
So I came to a solution:
def f(x=list()):
if len(x) == 0:
x = list()
print(id(x))
Solved! But at what price: In my opinion this doesn't seem to be a very clean solution.
This solution rely in the use of len(x) == 0 as a way to identify the default value which is Ok for my function but not for others so the solution can be generalized as:
def f(x=None):
if x is None:
x = list()
This can be shortened to:
def f(x=None):
x = x or list() # a bit shorter version
My question is, is there any shorter or better way to solve this problem? Will it ever be?
I still prefer the is None approach, but here is a new option to think about: If is defined only the type, you create a new instance of it.
def f(x=list):
if isinstance(x, type): x = x()
print(id(x))

How to initialize an instance of a subclass of tuple in Python? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Subclassing Python tuple with multiple __init__ arguments
I want to define a class which inherits from tuple, and I want to be able to instantiate it using a syntax not supported by tuple. For a simple example, let's say I want to define a class MyTuple which inherits from tuple, and which I can instantiate by passing two values, x and y, to create the (my) tuple (x, y). I've tried the following code:
class MyTuple(tuple):
def __init__(self, x, y):
print("debug message")
super().__init__((x, y))
But when I tried, for example, MyTuple(2, 3) I got an error: TypeError: tuple() takes at most 1 argument (2 given). It seems my __init__ function was not even called (based on the error I got and on the fact my "debug message" was not printed).
So what's the right way to do this?
I'm using Python 3.2.
class MyTuple(tuple):
def __new__(cls, x, y):
return tuple.__new__(cls, (x, y))
x = MyTuple(2,3)
print(x)
# (2, 3)
One of the difficulties of using super is that you do not control which classes's method of the same name is going to be called next. So all the classes' methods have to share the same call signature -- at least the same number of items. Since you are changing the number of arguments sent to __new__, you can not use super.
Or as Lattyware suggests, you could define a namedtuple,
import collections
MyTuple = collections.namedtuple('MyTuple', 'x y')
p = MyTuple(2,3)
print(p)
# MyTuple(x=2, y=3)
print(p.x)
# 2
another approach would be to encapsulate a tuple rather than inheriting from it:
>>> class MyTuple(object):
count = lambda self, *args: self._tuple.count(*args)
index = lambda self, *args: self._tuple.index(*args)
__repr__ = lambda self: self._tuple.__repr__()
# wrap other methods you need, or define them yourself,
# or simply forward all unknown method lookups to _tuple
def __init__(self, x, y):
self._tuple = x,y
>>> x = MyTuple(2,3)
>>> x
(2, 3)
>>> x.index(3)
1
How practical this is, depends on how many capabilities and modifications you need, and wheter you need to have isinstance(MyTuple(2, 3), tuple).

How to test whether x is a member of a universal set?

I have a list L, and x in L evaluates to True if x is a member of L. What can I use instead of L in order x in smth will evaluate to True independently on the value of x?
So, I need something, what contains all objects, including itself, because x can also be this "smth".
class Universe:
def __contains__(_,x): return True
You can inherit from the built-in list class and redefine the __contains__ method that is called when you do tests like item in list:
>>> class my_list(list):
def __contains__(self, item):
return True
>>> L = my_list()
>>> L
[]
>>> x = 2
>>> x
2
>>> x in L
True
Theorem: There is no universal set.
Proof. Let X be a set such that X = {\empty, x} where x is every possible element in the domain. The question arises, is X \in X? Most sets are not defined that way, so let us define a new set Y. Y = {A \in X; A \notin A} i.e. Y is the set of all sets not belonging to themselves.
Now, does Y \in Y? Well, we have defined Y as all sets not belonging to themselves, so Y cannot exist in Y, which contradicts our assumption.
So now assume Y is not in Y. Now A definitely contains Y, as Y is not in itself, but the definition of Y is such that if we define Y to be in Y, we contradict our own definition.
Thus, there is no set of all sets. This is known as Russell's Paradox.
So, why programmatically try to create an object that violates a result proved and tested by set theorists far more intelligent than I am? If that was my interview, this would be my answer and if they insisted it was possible, I'd suggest explaining what the problem domain is, since conceptually Russell has fundamentally proved it is impossible.
If you want a user-friendly problem usually posed for people studying introductory set theory, try the Barber Paradox.
Edit: Python lets you implement an object that contains itself. See this:
class Universal(object):
def __init__(self):
self.contents = []
def add(self, x):
self.contents.append(x)
def remove(self, x):
self.contents.remove(x)
def __contains__(self, x):
return ( x in self.contents )
However, this is not a strict set theoretic object, since the contents actually contains a reference to the parent object. If you require that objects be distinct as per the proof above, this cannot happen.

Indexer with two keys in python

I'm newbie with python. I want to write a class with two keys as indexer. also need to be able to use them inside of class like this:
a = Cartesian(-10,-10,10,10) # Cartesian is the name of my class
a[-5][-1]=10
and in the Cartesian class:
def fill(self,value):
self[x][y] = x*y-value
I try with
def __getitem__(self,x,y):
return self.data[x-self.dx][y-self.dy]
but doesn't work.
If you just need a lightweight application, you can have __getitem__ accept a tuple:
def __getitem__(self, c):
x, y = c
return self.data[x-self.dx][y-self.dy]
def __setitem__(self, c, v):
x, y = c
self.data[x-self.dx][y-self.dy] = v
and use like this:
a[-5,-1] = 10
However, if you are doing a lot of numeric computation or this is integral to your application, consider using Numpy and just represent this coordinate as a vector: Link
Is there any reason you actually need to explicitly define a Cartesian() class? For example, are there calculation methods on it? If not, then just use a lists within lists to use this type of syntax.
If you do need a class, then consider adding a .coordinate(x, y) method to it instead and don't bother trying to do the list syntax.
Accept a tuple:
>>> class Foo(object):
... def __getitem__(self, key):
... x, y = key
... print x, y
... f = Foo()
... f[1,2]
1 2

Possible to use more than one argument on __getitem__?

I am trying to use
__getitem__(self, x, y):
on my Matrix class, but it seems to me it doesn't work (I still don't know very well to use python).
I'm calling it like this:
print matrix[0,0]
Is it possible at all to use more than one argument? Thanks. Maybe I can use only one argument but pass it as a tuple?
__getitem__ only accepts one argument (other than self), so you get passed a tuple.
You can do this:
class matrix:
def __getitem__(self, pos):
x,y = pos
return "fetching %s, %s" % (x, y)
m = matrix()
print m[1,2]
outputs
fetching 1, 2
See the documentation for object.__getitem__ for more information.
Indeed, when you execute bla[x,y], you're calling type(bla).__getitem__(bla, (x, y)) -- Python automatically forms the tuple for you and passes it on to __getitem__ as the second argument (the first one being its self). There's no good way[1] to express that __getitem__ wants more arguments, but also no need to.
[1] In Python 2.* you can actually give __getitem__ an auto-unpacking signature which will raise ValueError or TypeError when you're indexing with too many or too few indices...:
>>> class X(object):
... def __getitem__(self, (x, y)): return x, y
...
>>> x = X()
>>> x[23, 45]
(23, 45)
Whether that's "a good way" is moot... it's been deprecated in Python 3 so you can infer that Guido didn't consider it good upon long reflection;-). Doing your own unpacking (of a single argument in the signature) is no big deal and lets you provide clearer errors (and uniform ones, rather than ones of different types for the very similar error of indexing such an instance with 1 vs, say, 3 indices;-).
No, __getitem__ just takes one argument (in addition to self). In the case of matrix[0, 0], the argument is the tuple (0, 0).
You can directly call __getitem__ instead of using brackets.
Example:
class Foo():
def __init__(self):
self.a = [5, 7, 9]
def __getitem__(self, i, plus_one=False):
if plus_one:
i += 1
return self.a[I]
foo = Foo()
foo[0] # 5
foo.__getitem__(0) # 5
foo.__getitem__(0, True) # 7
I learned today that you can pass double index to your object that implements getitem, as the following snippet illustrates:
class MyClass:
def __init__(self):
self.data = [[1]]
def __getitem__(self, index):
return self.data[index]
c = MyClass()
print(c[0][0])

Categories