Which types of objects fall into the domain of "subscriptable"?
It basically means that the object implements the __getitem__() method. In other words, it describes objects that are "containers", meaning they contain other objects. This includes strings, lists, tuples, and dictionaries.
Off the top of my head, the following are the only built-ins that are subscriptable:
string: "foobar"[3] == "b"
tuple: (1,2,3,4)[3] == 4
list: [1,2,3,4][3] == 4
dict: {"a":1, "b":2, "c":3}["c"] == 3
But mipadi's answer is correct - any class that implements __getitem__ is subscriptable
The meaning of subscript in computing is:
"a symbol (notionally written as a subscript but in practice usually not) used in a program, alone or with others, to specify one of the elements of an array."
Now, in the simple example given by #user2194711 we can see that the appending element is not able to be a part of the list because of two reasons:-
1) We are not really calling the method append; because it needs () to call it.
2) The error is indicating that the function or method is not subscriptable; means they are not indexable like a list or sequence.
Now see this:-
>>> var = "myString"
>>> def foo(): return 0
...
>>> var[3]
't'
>>> foo[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'function' object is not subscriptable
That means there are no subscripts or say elements in function like they occur in sequences; and we cannot access them like we do, with the help of [].
Also; as mipadi said in his answer; It basically means that the object implements the __getitem__() method. (if it is subscriptable).
Thus the error produced:
arr.append["HI"]
TypeError: 'builtin_function_or_method' object is not subscriptable
A scriptable object is an object that records the operations done to it and it can store them as a "script" which can be replayed.
For example, see: Application Scripting Framework
Now, if Alistair didn't know what he asked and really meant "subscriptable" objects (as edited by others), then (as mipadi also answered) this is the correct one:
A subscriptable object is any object that implements the __getitem__ special method (think lists, dictionaries).
I had this same issue. I was doing
arr = []
arr.append["HI"]
So using [ was causing error. It should be arr.append("HI")
As a corollary to the earlier answers here, very often this is a sign that you think you have a list (or dict, or other subscriptable object) when you do not.
For example, let's say you have a function which should return a list;
def gimme_things():
if something_happens():
return ['all', 'the', 'things']
Now when you call that function, and something_happens() for some reason does not return a True value, what happens? The if fails, and so you fall through; gimme_things doesn't explicitly return anything -- so then in fact, it will implicitly return None. Then this code:
things = gimme_things()
print("My first thing is {0}".format(things[0]))
will fail with "NoneType object is not subscriptable" because, well, things is None and so you are trying to do None[0] which doesn't make sense because ... what the error message says.
There are two ways to fix this bug in your code -- the first is to avoid the error by checking that things is in fact valid before attempting to use it;
things = gimme_things()
if things:
print("My first thing is {0}".format(things[0]))
else:
print("No things") # or raise an error, or do nothing, or ...
or equivalently trap the TypeError exception;
things = gimme_things()
try:
print("My first thing is {0}".format(things[0]))
except TypeError:
print("No things") # or raise an error, or do nothing, or ...
Another is to redesign gimme_things so that you make sure it always returns a list. In this case, that's probably the simpler design because it means if there are many places where you have a similar bug, they can be kept simple and idiomatic.
def gimme_things():
if something_happens():
return ['all', 'the', 'things']
else: # make sure we always return a list, no matter what!
logging.info("Something didn't happen; return empty list")
return []
Of course, what you put in the else: branch depends on your use case. Perhaps you should raise an exception when something_happens() fails, to make it more obvious and explicit where something actually went wrong? Adding exceptions to your own code is an important way to let yourself know exactly what's up when something fails!
(Notice also how this latter fix still doesn't completely fix the bug -- it prevents you from attempting to subscript None but things[0] is still an IndexError when things is an empty list. If you have a try you can do except (TypeError, IndexError) to trap it, too.)
Basically this error will appear in case you are modifying or adding any field after type casting for the mentioned object instead of doing it before.
Related
Background
I am new to python and I am writing a simple function but I am also interested in learning to do things the correct / pythonic way as I progress in my journey.
Lets consider the function below
def test_func(nested_lists,val):
return
I am expecting two arguments. One argument would be a list containing more lists. Something like this [[1,2,3,],[4,5,6,]...]. The second argument could be a value like 1.
If someone say for instance passes in a single value as the first argument and an array as the second argument. My code as it is currently returning the correct output which is 0 , However is there another way that i should be handle this?
For example should I be doing something like this
if(type(value) == list):
return 0
Or do i not need to do anything because my function is returning 0 anyway.
I know this maybe a very basic question so please forgive me but coming from a java background I am new to python so i am not sure how to handle such scenarios in python.
The other answer illustrates the proper way to check in advance for problems you can foresee. I'll provide a different approach.
The idiomatic solution in python is to "ask forgiveness, not permission". There are a lot of things that can go wrong, and where other languages might ask you to foresee all those problems and address them manually, python encourages just handling them as they happen. I would recommend doing:
def test_func(nested_lists, val):
try:
...
except TypeError:
# do whatever error-handling behavior you need to
# either throw a custom exception or return a specific value or whatever
return 0
and then designing your code in such a way that, if nested_lists and values are not compatible types, then they throw a TypeError (e.g. trying to iterate through nested_lists should fail if nested_lists is not a list. You can experiment with this behavior in a python console, but in general trying to do something to a variable that doesn't work because it's not the right type will produce a TypeError).
If your current code is working correctly, there is no pressing need to change anything. However, there are some reasons you might want to code more defensively;
If the code will seem to work correctly when you pass in bogus values, it would be better if it raised an exception instead of return a bogus value. The responsibility to call it correctly lies squarely with the caller, but enforcing it can help make sure the code is correct.
if not isinstance(nested_lists,list):
raise ValueError('Need a list, got {0!r}'.format(nested_lists))
This has the drawback that it hardcodes list as the type for the first argument; properly reusable code should work with any type, as long as it has the required methods and behaviors to remain compatible with your implementation. Perhaps instead check for a behavior:
try:
something involving nested_lists[0][0]
except (IndexError, AttributeError):
raise ValueError('Expected nested list but got {0!r}'.format(nested_lists))
(The try is not strictly necessary here; but see below.)
If you get a traceback when you call the code incorrectly, but it is opaque or misleading, it is more helpful to catch and explicitly point out the error earlier. #or example, the snippet above (without the try wrapper) would produce
Traceback (most recent call last):
module __main__ line 141
traceback.print_exc()
module <module> line 1
test_func(1,1)
module <module> line 2
AttributeError: 'int' object has no attribute '__getitem__'
which is somewhat unobvious to debug.
If the code will be used by third parties, both of the above considerations will be more important from a support point of view, too.
Notice how the code raises an exception when called incorrectly. This is generally better than silently returning some garbage value, and the caller can similarly trap the error with a try/except if this is well-defined (i.e. documented!) behavior.
Finally, since Python 3.5, you have the option to use type annotations:
def test_func(nested_lists: list, val: int) -> int:
...
As noted in the documentation, the core language does not (yet?) enforce these type checks, but they can help static code analysis tools point out possible errors.
Suppose we try to access a non-existing attribute:
>>> {'foo': 'bar'}.gte('foo') # well, I meant “get”!
Python’s AttributeError only has the attribute args with a string containing the finished error message: 'dict' object has no attribute 'gte'
Using the inspect and/or traceback modules with sys.last_traceback, is there a way to get hold of the actual dict object?
>>> offending_object = get_attributeerror_obj(sys.last_traceback)
>>> dir(offending_object)
[...
'clear',
'copy',
'fromkeys',
'get', # ah, here it is!
'items',
...]
Edit: since the cat is out of the bag anyway, I’ll share my findings and code (please don’t solve this and submit to PyPI, please ;))
The AttributeError is created here, which shows that there’s clearly no reference to the originating object attached.
Here the code with the same placeholder function:
import sys
import re
import difflib
AE_MSG_RE = re.compile(r"'(\w+)' object has no attribute '(\w+)'")
def get_attributeerror_obj(tb):
???
old_hook = sys.excepthook
def did_you_mean_hook(type, exc, tb):
old_hook(type, exc, tb)
if type is AttributeError:
match = AE_MSG_RE.match(exc.args[0])
sook = match.group(2)
raising_obj = get_attributeerror_obj(tb)
matches = difflib.get_close_matches(sook, dir(raising_obj))
if matches:
print('\n\nDid you mean?', matches[0], file=sys.stderr)
sys.excepthook = did_you_mean_hook
It's not the answer you want, but I'm pretty sure you can't... at least not with sys.excepthook. This is because the reference counts are decremented as the frame is unwound, so it's perfectly valid for the object to be garbage collected before sys.excepthook is called. In fact, this is what happens in CPython:
import sys
class X:
def __del__(self):
print("deleting")
def error():
X().wrong
old_hook = sys.excepthook
def did_you_mean_hook(type, exc, tb):
print("Error!")
sys.excepthook = did_you_mean_hook
error()
#>>> deleting
#>>> Error!
That said, it isn't always the case. Because the exception object points to the frame, if your code looks like:
def error():
x = X()
x.wrong
x cannot yet be collected. x is owned by the frame, and the frame is alive. But since I've already proven that there is no explicit reference made to this object, it's not ever obvious what to do. For example,
def error():
foo().wrong
may or may not have an object that has survived, and the only feasible way to find out is to run foo... but even then you have problems with side effects.
So no, this is not possible. If you don't mind going to any lengths whatsoever, you'll probably end up having to rewrite the AST on load (akin to FuckIt.py). You don't want to do that, though.
My suggestion would be to try using a linter to get the names of all known classes and their methods. You can use this to reverse-engineer the traceback string to get the class and incorrect method, and then run a fuzzy match to find the suggestion.
Adding my 2 cents as I successfully (so far) tried to do something similar for DidYouMean-Python.
The trick here is that it is pretty much the one case where the error message contains enough information to infer what you actually meant. Indeed, what really matters here is that you tried to call gte on a dict object : you need the type, not the object itself.
If you had written {'foo': 'bar'}.get('foob') the situation would be much trickier to handle and I'd be happy to know if anyone had a solution.
Step one
Check that you are handling an AttributeError (using the first argument of the hook).
Step two
Retrieve the relevant information from the message (using the second argument). I did this with regexp. Please note that this exception can take multiple forms depending on the version of Python, the object you are calling the method on, etc.
So far, my regexp is : "^'?(\w+)'? (?:object|instance) has no attribute '(\w+)'$"
Step three
Get the type object corresponding to the type ('dict' in your case) so that you can call dir() on it. A dirty solution would be just use eval(type) but you can do better and cleaner by reusing the information in the trace (third argument of your hook) : the last element of the trace contains the frame in which the exception occured and in that frame, the type was properly defined (either as a local type, a global type or a builtin).
Once you have the type object, you just need to call dir() on it and extract the suggestion you like the most.
Please let me know if you need more details on what I did.
I am still learning the Python programmimg language. I asked myself in terms of code exceptions, when it is neccessary to handle such situations in a pythonic way. I read a few times "you should never pass an error silently".
For example a little function:
def square(list_with_items):
return [i**2 for i in list_with_items]
Is it neccessary to write an error-handler, if somebody passes a tuple as parameter?
Or it is more senseful to do it, when I have to check the validation of user-input?
In the specific case of checking types, the "Pythonic" thing to do is not to check them. Unless there is a good reason, you should assume that the caller is passing in a sensible type (note: "sensible type" might be different from "the type you expect"), and do your best to return something sensible as well. If the caller passes in a type that isn't sensible, it's perfectly acceptable to let them deal with the consequences.
For example, someone might sensibly pass an iterator of Decimal numbers into your square function:
>>> from decimal import Decimal
>>> square(Decimal(line.strip()) for line in open("numbers.txt")
[Decimal("4.0"), Decimal("9.0"), ...]
And everything would work! But explicitly checking the types would make that use case more difficult.
And then, for example, if someone passes in something that isn't sensible, they can deal with the error:
>>> square(42)
…
TypeError: 'int' object isn't iterable
This error message will also (in a script) contain all the file names and line numbers necessary to debug the issue.
On the other hand, it is sometimes useful to explicitly check the arguments, when the caller might make a mistake with surprising consequences. For example, if you're writing a function which will exhibit very poor performance with a list because it expects a deque, then a check for if not isinstance(input, deque): raise TypeError("a deque must be used!") might be justified.
The name for this "method for dealing with types" is called Duck Typing.
You could use an assertion:
def square(list_with_items):
assert(all([type(x) == int for x in list_with_items]))
return [i**2 for i in list_with_items]
You could use two assertions:
def square(list_with_items):
assert(type(list_with_items) in (list, tuple))
assert(all([type(x) == int for x in list_with_items]))
return [i**2 for i in list_with_items]
You could use an assertion and a try/catch:
def square(list_with_items):
assert(type(list_with_items) in (list, tuple))
try:
return [i**2 for i in list_with_items]
except TypeError:
raise Exception("Each element of the argument must be a number.")
It depends. In that specific example, if you are going to use the return value of "square" in "read-only" manner, you shouldn't have a "real bug" passing a tuple as argument. That's because even if the tuple is immutable, your function will return a new tuple with the square of the elements of the first one.
Anyhow, I suggest you to use a simple if-else statement, to be more precise and to avoid issues (i.e.: if you are going to call this function in a different way in future):
def square(list_with_items):
if isinstance(list_with_items, list): # True if list_with_items is a list - False otherwise
return [i**2 for i in list_with_items]
else:
return 'Error: type(list_with_items) must be a list!'
EDIT:
If you prefer (and if you are going to catch the exception with try-except statement), you could use "raise Exception" in the else-statement:
else:
raise Exception('Error: type(list_with_items) must be a list!')
I'm new to python and I would like some help. I created some classes with properties in order to keep me from passing meaningless arguments.
So for example I have this class
class match(object):
__teams=(None,None)
def setTeams(self,tms):
if type(tms) != type(list()) and type(tms) != type(tuple()):
raise Exception("Teams must be a list of length 2")
if len(tms) != 2:
raise Exception("Teams must be a list of length 2")
if (type(tms[0])==type(str()) or (type(tms[0])==type(unicode()))) \
and (type(tms[1])==type(str()) or type(tms[1])==type(unicode())):
self.__teams=tms
else:
raise Exception("Both teams must be strings")
return
teams=property(getTeams,setTeams)
If I write
match1=match()
match1.teams=(2,4)
I get an exception as I should, but
match1.teams[0]=5
does not raise an exception and passes the number 5. Please keep in mind that this is not all of the class, I just wrote down only what is relative to my question, so assume that the code behaves as I describe.
I guess this is because everything is passed by reference in python but I have to be careful not to assign meaningless data to my objects which defeats the purpose of having properties in the first place.
So, is there a way to fix that apart from not using lists or do I have to learn to live with it?
This error isn't because you are failing some typecheck.
Unless you have misrepresented your code (it's obviously edited, as what you posted won't run correctly), this is happening because match1.teams[0] calls your getTeams function, not your setTeams function. To see this for yourself, try this exercise:
class match(object):
__teams=(None,None)
def setTeams(self,tms):
print "in set"
self.__teams = tms
def getTeams(self):
print "in get"
return self.__teams
teams=property(getTeams,setTeams)
When I try this, I get the following:
>>> match1 = match()
>>> match1.teams[0]=5
in get
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> match1.teams = ["team1","team2"]
in set
>>> match1.teams[0]=5
in get
Python and type checks don't go together. Learn to live with it. It's the job of whoever uses the code to pass the proper types. Document what your code expects, but don't check it explicitly.
There are other collections than lists and tuples. Why would you forbid, say, namedtuple? Python is a dynamic language, don't fight it by writing type checks.
Look up EAFP in the Python glossary. Don't try to anticipate errors; deal with them as they happen.
A reasonable thing you might do instead of type checking is converting to a list:
self.__teams = list(tms)
List-incompatible types will cause an exception to be raised on this line, and from now on you can be sure you're dealing with a list.
(It won't prevent someone from assigning non-strings to the list, of course.)
Oh, and if you ever (with good reason!) need a type check, use the isinstance function instead of comparing the type(). That will also catch subclasses of whatever your required type is. And more, try to use the most general base type you can. The proper way to test for a string (Unicode or otherwise) is:
if isinstance(my_object, basestring):
....
And the proper way to check for a list-like collection – not just a narrow-minded “list or tuple” – is:
import collections
if isinstance(my_object, collections.Sequence):
...
But that was just an aside, not the proper solution to your problem. Don't do type checking without good reason.
One of the advantages of propertys is being able to do data validation -- sometimes it is really important to make sure you get something very specific.
In your case you need to do one of two things:
store your teams data in a structure that can't be modified, such as a tuple or namedtuple; then when the data is retrieved it cannot be changed
or
have your get method return a copy of the data, so any modification do not mess up your original
The first solution (immutable types) looks like this:
class match(object):
__teams=(None,None)
def setTeams(self,tms):
"any sequence type will do, as long as length is two"
if len(tms) != 2:
raise TypeError(
"Teams must be a sequence of length 2"
)
if not isinstance(tms[0], (str, unicode)):
raise TypeError(
"Team names must be str or unicode, not %r" % type(tms[0])
)
if not isinstance(tms[1], (str, unicode)):
raise TypeError(
"Team names must be str or unicode, not %r" % type(tms[0])
)
self.__teams = tuple(tms)
def getTeams(self):
return self.__teams
teams=property(getTeams,setTeams)
And when you try to assign after getting the value, this happens:
Traceback (most recent call last):
File "test.py", line 22, in <module>
match1.teams[0]=5
TypeError: 'tuple' object does not support item assignment
The second solution (returning a copy instead of the original) looks like this:
class match(object):
__teams=(None,None)
def setTeams(self,tms):
"any sequence type will do, as long as length is two"
if len(tms) != 2:
raise TypeError(
"Teams must be a sequence of length 2"
)
if not isinstance(tms[0], (str, unicode)):
raise TypeError(
"Team names must be str or unicode, not %r" % type(tms[0])
)
if not isinstance(tms[1], (str, unicode)):
raise TypeError(
"Team names must be str or unicode, not %r" % type(tms[0])
)
self.__teams = list(tms)
def getTeams(self):
return list(self.__teams)
teams=property(getTeams,setTeams)
# and the code in action...
match1=match()
match1.teams=('us',u'them')
match1.teams[0]=5
print match1.teams
which has the following results:
['us', u'them']
As you can see, the changes did not make it back into the match object.
Which types of objects fall into the domain of "subscriptable"?
It basically means that the object implements the __getitem__() method. In other words, it describes objects that are "containers", meaning they contain other objects. This includes strings, lists, tuples, and dictionaries.
Off the top of my head, the following are the only built-ins that are subscriptable:
string: "foobar"[3] == "b"
tuple: (1,2,3,4)[3] == 4
list: [1,2,3,4][3] == 4
dict: {"a":1, "b":2, "c":3}["c"] == 3
But mipadi's answer is correct - any class that implements __getitem__ is subscriptable
The meaning of subscript in computing is:
"a symbol (notionally written as a subscript but in practice usually not) used in a program, alone or with others, to specify one of the elements of an array."
Now, in the simple example given by #user2194711 we can see that the appending element is not able to be a part of the list because of two reasons:-
1) We are not really calling the method append; because it needs () to call it.
2) The error is indicating that the function or method is not subscriptable; means they are not indexable like a list or sequence.
Now see this:-
>>> var = "myString"
>>> def foo(): return 0
...
>>> var[3]
't'
>>> foo[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'function' object is not subscriptable
That means there are no subscripts or say elements in function like they occur in sequences; and we cannot access them like we do, with the help of [].
Also; as mipadi said in his answer; It basically means that the object implements the __getitem__() method. (if it is subscriptable).
Thus the error produced:
arr.append["HI"]
TypeError: 'builtin_function_or_method' object is not subscriptable
A scriptable object is an object that records the operations done to it and it can store them as a "script" which can be replayed.
For example, see: Application Scripting Framework
Now, if Alistair didn't know what he asked and really meant "subscriptable" objects (as edited by others), then (as mipadi also answered) this is the correct one:
A subscriptable object is any object that implements the __getitem__ special method (think lists, dictionaries).
I had this same issue. I was doing
arr = []
arr.append["HI"]
So using [ was causing error. It should be arr.append("HI")
As a corollary to the earlier answers here, very often this is a sign that you think you have a list (or dict, or other subscriptable object) when you do not.
For example, let's say you have a function which should return a list;
def gimme_things():
if something_happens():
return ['all', 'the', 'things']
Now when you call that function, and something_happens() for some reason does not return a True value, what happens? The if fails, and so you fall through; gimme_things doesn't explicitly return anything -- so then in fact, it will implicitly return None. Then this code:
things = gimme_things()
print("My first thing is {0}".format(things[0]))
will fail with "NoneType object is not subscriptable" because, well, things is None and so you are trying to do None[0] which doesn't make sense because ... what the error message says.
There are two ways to fix this bug in your code -- the first is to avoid the error by checking that things is in fact valid before attempting to use it;
things = gimme_things()
if things:
print("My first thing is {0}".format(things[0]))
else:
print("No things") # or raise an error, or do nothing, or ...
or equivalently trap the TypeError exception;
things = gimme_things()
try:
print("My first thing is {0}".format(things[0]))
except TypeError:
print("No things") # or raise an error, or do nothing, or ...
Another is to redesign gimme_things so that you make sure it always returns a list. In this case, that's probably the simpler design because it means if there are many places where you have a similar bug, they can be kept simple and idiomatic.
def gimme_things():
if something_happens():
return ['all', 'the', 'things']
else: # make sure we always return a list, no matter what!
logging.info("Something didn't happen; return empty list")
return []
Of course, what you put in the else: branch depends on your use case. Perhaps you should raise an exception when something_happens() fails, to make it more obvious and explicit where something actually went wrong? Adding exceptions to your own code is an important way to let yourself know exactly what's up when something fails!
(Notice also how this latter fix still doesn't completely fix the bug -- it prevents you from attempting to subscript None but things[0] is still an IndexError when things is an empty list. If you have a try you can do except (TypeError, IndexError) to trap it, too.)
Basically this error will appear in case you are modifying or adding any field after type casting for the mentioned object instead of doing it before.