Possible to use more than one argument on __getitem__? - python

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])

Related

Most pythonic way to provide defaults for class constructor

I am trying to stick to Google's styleguide to strive for consistency from the beginning.
I am currently creating a module and within this module I have a class. I want to provide some sensible default values for different standard use cases. However, I want to give the user the flexibility to override any of the defaults. What I am currently doing is I provide a module scoped "constant" dictionary with the default values (for the different use cases) and in my class I give the parameters in the constructor precedence over the defaults.
Finally, I want to make sure that we end with valid values for the parameters.
That's what I have done:
MY_DEFAULTS = {"use_case_1": {"x": 1, "y": 2},
"use_case_2": {"x": 4, "y": 3}}
class MyClass:
def __init__(self, use_case = None, x = None, y = None):
self.x = x
self.y = y
if use_case:
if not self.x:
self.x = MY_DEFAULTS[use_case]["x"]
if not self.y:
self.y = MY_DEFAULTS[use_case]["y"]
assert self.x, "no valid values for 'x' provided"
assert self.y, "no valid values for 'y' provided"
def __str__(self):
return "(%s, %s)" % (self.x, self.y)
print(MyClass()) # AssertionError: no valid values for 'x' provided
print(MyClass("use_case_1")) # (1, 2)
print(MyClass("use_case_2", y = 10) # (4, 10)
Questions
While technically working, I was wondering whether this is the most pythonic way of doing it?
With more and more default values for my class the code becomes very repetitive, what could I do to simplify that?
assert seems also for me not the best option at it is rather a debugging statement than a validation check. I was toying with the #property decorator, where I would raise an Exception in case there are invalid parameters, but with the current pattern I want to allow x and y for a short moment to be not truthy to implement the precedence properly (that is I only want to check the truthiness at the end of the constructor. Any hints on that?
In general if there is more than one way to reasonably construct your object type, you can provide classmethods for alternate construction (dict.fromkeys is an excellent example of this). Note that this approach is more applicable if your use cases are finite and well defined statically.
class MyClass:
def __init__(self, x, y):
self.x = x
self.y = y
#classmethod
def make_use_case1(cls, x=1, y=2):
return cls(x,y)
#classmethod
def make_use_case2(cls, x=4, y=3):
return cls(x,y)
def __str__(self):
return "(%s, %s)" % (self.x, self.y)
If the only variation in the use cases is default arguments then re-writing the list of positional arguments each time is a lot of overhead. Instead we can write one classmethod to take the use case and the optional overrides as keyword only.
class MyClass:
DEFAULTS_PER_USE_CASE = {
"use_case_1": {"x": 1, "y": 2},
"use_case_2": {"x": 4, "y": 3}
}
#classmethod
def make_from_use_case(cls, usecase, **overrides):
args = {**cls.DEFAULTS_PER_USE_CASE[usecase], **overrides}
return cls(**args)
def __init__(self, x,y):
self.x = x
self.y = y
def __str__(self):
return "(%s, %s)" % (self.x, self.y)
x = MyClass.make_from_use_case("use_case_1", x=5)
print(x)
If you wanted the arguments to be passed positionally that would be more difficult but I imagine this would suit your needs.
Python is a very flexible language. If your code runs, there is no technically wrong way of doing things. However, if you want to be "Pythonic", here are a few tips for you. First of all, you should never use AssertionErrors for verifying the presence or value of a parameter. If a parameter is not passed and it should be there, you should raise a TypeError. If the value passed is not acceptable, you should raise a ValueError. Assertions are mainly used for testing.
When you want to verify the presence of a value in the parameter a, it is best to do a is not None, rather than not a. You can do not a when None and 0 or other Falsy values are equally invalid for you. However, when the purpose is to check the presence of a value, 0 and None are not the same.
Regarding your class, I believe that a nicer way of doing this is unwrapping the values of the dictionary upon the class initalization. If you remove use_case from the function signature, and call your class like this:
MyClass(**MY_DEFAULTS["use_case_1"])
Python will unwrap the values of the nested dictionary and pass them as keyword arguments to your __init__ method. If you do not want the values to be optional, remove the default value and Python will raise a TypeError for you if the parameters provided do not match the function signature.
If you still want your parameters to not be Falsy, perhaps you should want to provide a more concrete scope for the possible values of the parameters. If the type of x is int, and you don't want 0 values, then you should compare x with 0:
def __init__(x, y):
if x == 0 or y == 0:
raise ValueError("x or y cannot be 0")
keeping your original interface, you could use kwargs to read parameters. If some are missing, set the defaults, only if the use case matches.
MY_DEFAULTS = {"use_case_1": {"x": 1, "y": 2},
"use_case_2": {"x": 4, "y": 3}}
class MyClass:
def __init__(self, use_case = None, **kwargs):
for k,v in kwargs.items():
setattr(self,k,v)
if use_case:
for k,v in MY_DEFAULTS[use_case].items():
if k not in kwargs:
setattr(self,k,v)
unassigned = {'x','y'}
unassigned.difference_update(self.__dict__)
if unassigned:
raise TypeError("missing params: {}".format(unassigned))
def __str__(self):
return "(%s, %s)" % (self.x, self.y)
print(MyClass("use_case_1")) # (1, 2)
print(MyClass("use_case_2", y = 10)) # (4, 10)
print(MyClass())
executing this:
(1, 2)
(4, 10)
Traceback (most recent call last):
File "<string>", line 566, in run_nodebug
File "C:\Users\T0024260\Documents\module1.py", line 22, in <module>
print(MyClass())
File "C:\Users\T0024260\Documents\module1.py", line 15, in __init__
raise TypeError("missing params: {}".format(unassigned))
TypeError: missing params: {'y', 'x'}
With more and more default values for my class the code becomes very repetitive, what could I do to simplify that?
This solution allows to have many parameters.

Python thinks I'm passing 3 arguments although I'm only passing 2

I've written a basic Vector class for dealing with vectors. Calculating their magnitude and whatnot. I have two overloaded constructors one taking two arguments x and y and one taking a tuple (x,y).
When I initialize a variable it gives me the error:
TypeError: __init__() takes 2 positional arguments but 3 were given.
What am I missing?
class Vector:
x = 0.0
y = 0.0
def __init__(self, x, y):
self.x = x
self.y = y
def __init__(self, coordinates):
self.x = coordinates[0]
self.y = coordinates[1]
v1 = Vector(1,3)
print(v1.x)
Python doesn't support overloading, so you overwrite the first __init__ method with the second one. The second one takes only 1 argument: coordinates. That Python writes 3 arguments is because the instance (self) is passed implicitly.
Remove the second __init__ method and it will work correctly.
If you like multiple constructors you could for example implement the additional ones as classmethods:
class Vector:
x = 0.0
y = 0.0
def __init__(self, x, y):
self.x = x
self.y = y
#classmethod
def from_list(cls, coordinates):
return cls(coordinates[0], coordinates[1])
# you could also use unpacking:
# return cls(*coordinates)
Test:
>>> v1 = Vector(1,3)
>>> print(v1.x)
1
>>> v2 = Vector.from_list([2, 1])
>>> print(v2.x)
2
Python doesn't do overloaded constructors - its a dynamic language and has no preknowledge of type that would allow this to happen. Your code defines an __init__ then replaces it completely with a second __init__. If you want to do something like this, you could define a factory method with the alternate signature and call that.
You do not have overloaded constructors: the second method definition overwrites the first. Thus, your only surviving constructor takes only two arguments. You supplied 3 -- self is implied.

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

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])

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).

Assign function arguments to `self`

I've noticed that a common pattern I use is to assign SomeClass.__init__() arguments to self attributes of the same name. Example:
class SomeClass():
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
In fact it must be a common task for others as well as PyDev has a shortcut for this - if you place the cursor on the parameter list and click Ctrl+1 you're given the option to Assign parameters to attributes which will create that boilerplate code for you.
Is there a different, short and elegant way to perform this assignment?
You could do this, which has the virtue of simplicity:
>>> class C(object):
def __init__(self, **kwargs):
self.__dict__ = dict(kwargs)
This leaves it up to whatever code creates an instance of C to decide what the instance's attributes will be after construction, e.g.:
>>> c = C(a='a', b='b', c='c')
>>> c.a, c.b, c.c
('a', 'b', 'c')
If you want all C objects to have a, b, and c attributes, this approach won't be useful.
(BTW, this pattern comes from Guido his own bad self, as a general solution to the problem of defining enums in Python. Create a class like the above called Enum, and then you can write code like Colors = Enum(Red=0, Green=1, Blue=2), and henceforth use Colors.Red, Colors.Green, and Colors.Blue.)
It's a worthwhile exercise to figure out what kinds of problems you could have if you set self.__dict__ to kwargs instead of dict(kwargs).
I sympathize with your sense that boilerplate code is a bad thing. But in this case, I'm not sure there even could be a better alternative. Let's consider the possibilities.
If you're talking about just a few variables, then a series of self.x = x lines is easy to read. In fact, I think its explicitness makes that approach preferable from a readability standpoint. And while it might be a slight pain to type, that alone isn't quite enough to justify a new language construct that might obscure what's really going on. Certainly using vars(self).update() shenanigans would be more confusing than it's worth in this case.
On the other hand, if you're passing nine, ten, or more parameters to __init__, you probably need to refactor anyway. So this concern really only applies to cases that involve passing, say, 5-8 parameters. Now I can see how eight lines of self.x = x would be annoying both to type and to read; but I'm not sure that the 5-8 parameter case is common enough or troublesome enough to justify using a different method. So I think that, while the concern you're raising is a good one in principle, in practice, there are other limiting issues that make it irrelevant.
To make this point more concrete, let's consider a function that takes an object, a dict, and a list of names, and assigns values from the dict to names from the list. This ensures that you're still being explicit about which variables are being assigned to self. (I would never suggest a solution to this problem that didn't call for an explicit enumeration of the variables to be assigned; that would be a rare-earth bug magnet):
>>> def assign_attributes(obj, localdict, names):
... for name in names:
... setattr(obj, name, localdict[name])
...
>>> class SomeClass():
... def __init__(self, a, b, c):
... assign_attributes(self, vars(), ['a', 'b', 'c'])
Now, while not horribly unattractive, this is still harder to figure out than a straightforward series of self.x = x lines. And it's also longer and more trouble to type than one, two, and maybe even three or four lines, depending on circumstances. So you only get certain payoff starting with the five-parameter case. But that's also the exact moment that you begin to approach the limit on human short-term memory capacity (= 7 +/- 2 "chunks"). So in this case, your code is already a bit challenging to read, and this would only make it more challenging.
Mod for #pcperini's answer:
>>> class SomeClass():
def __init__(self, a, b=1, c=2):
for name,value in vars().items():
if name != 'self':
setattr(self,name,value)
>>> s = SomeClass(7,8)
>>> print s.a,s.b,s.c
7 8 2
Your specific case could also be handled with a namedtuple:
>>> from collections import namedtuple
>>> SomeClass = namedtuple("SomeClass", "a b c")
>>> sc = SomeClass(1, "x", 200)
>>> print sc
SomeClass(a=1, b='x', c=200)
>>> print sc.a, sc.b, sc.c
1 x 200
Decorator magic!!
>>> class SomeClass():
#ArgsToSelf
def __init__(a, b=1, c=2, d=4, e=5):
pass
>>> s=SomeClass(6,b=7,d=8)
>>> print s.a,s.b,s.c,s.d,s.e
6 7 2 8 5
while defining:
>>> import inspect
>>> def ArgsToSelf(f):
def act(self, *args, **kwargs):
arg_names,_,_,defaults = inspect.getargspec(f)
defaults=list(defaults)
for arg in args:
setattr(self, arg_names.pop(0),arg)
for arg_name,arg in kwargs.iteritems():
setattr(self, arg_name,arg)
defaults.pop(arg_names.index(arg_name))
arg_names.remove(arg_name)
for arg_name,arg in zip(arg_names,defaults):
setattr(self, arg_name,arg)
return f(*args, **kwargs)
return act
Of course you could define this decorator once and use it throughout your project.Also, This decorator works on any object function, not only __init__.
You can do it via setattr(), like:
[setattr(self, key, value) for key, value in kwargs.items()]
Is not very beautiful, but can save some space :)
So, you'll get:
kwargs = { 'd':1, 'e': 2, 'z': 3, }
class P():
def __init__(self, **kwargs):
[setattr(self, key, value) for key, value in kwargs.items()]
x = P(**kwargs)
dir(x)
['__doc__', '__init__', '__module__', 'd', 'e', 'z']
For that simple use-case I must say I like putting things explicitly (using the Ctrl+1 from PyDev), but sometimes I also end up using a bunch implementation, but with a class where the accepted attributes are created from attributes pre-declared in the class, so that I know what's expected (and I like it more than a namedtuple as I find it more readable -- and it won't confuse static code analysis or code-completion).
I've put on a recipe for it at: http://code.activestate.com/recipes/577999-bunch-class-created-from-attributes-in-class/
The basic idea is that you declare your class as a subclass of Bunch and it'll create those attributes in the instance (either from default or from values passed in the constructor):
class Point(Bunch):
x = 0
y = 0
p0 = Point()
assert p0.x == 0
assert p0.y == 0
p1 = Point(x=10, y=20)
assert p1.x == 10
assert p1.y == 20
Also, Alex Martelli also provided a bunch implementation: http://code.activestate.com/recipes/52308-the-simple-but-handy-collector-of-a-bunch-of-named/ with the idea of updating the instance from the arguments, but that'll confuse static code-analysis (and IMO can make things harder to follow) so, I'd only use that approach for an instance that's created locally and thrown away inside that same scope without passing it anywhere else).
I solved it for myself using locals() and __dict__:
>>> class Test:
... def __init__(self, a, b, c):
... l = locals()
... for key in l:
... self.__dict__[key] = l[key]
...
>>> t = Test(1, 2, 3)
>>> t.a
1
>>>
Disclaimer
Do not use this: I was simply trying to create the answer closest to OPs initial intentions. As pointed out in comments, this relies on entirely undefined behavior, and explicitly prohibited modifications of the symbol table.
It does work though, and has been tested under extremely basic circumstances.
Solution
class SomeClass():
def __init__(self, a, b, c):
vars(self).update(dict((k,v) for k,v in vars().iteritems() if (k != 'self')))
sc = SomeClass(1, 2, 3)
# sc.a == 1
# sc.b == 2
# sc.c == 3
Using the vars() built-in function, this snippet iterates through all of the variables available in the __init__ method (which should, at this point, just be self, a, b, and c) and set's self's variables equal to the same, obviously ignoring the argument-reference to self (because self.self seemed like a poor decision.)
One of the problems with #user3638162's answer is that locals() contain the 'self' variable. Hence, you end up with an extra self.self. If one doesn't mind the extra self, that solution can simply be
class X:
def __init__(self, a, b, c):
self.__dict__.update(locals())
x = X(1, 2, 3)
print(x.a, x.__dict__)
The self can be removed after construction by del self.__dict__['self']
Alternatively, one can remove the self during construction using dictionary comprehensions introduced in Python3
class X:
def __init__(self, a, b, c):
self.__dict__.update(l for l in locals().items() if l[0] != 'self')
x = X(1, 2, 3)
print(x.a, x.__dict__)

Categories