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.
Related
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.
I want to include an except inside a class, but when I run the code, the except is returning an empty class object. How can I make the except return only the error message and nothing else?
class InputError(Exception):
"""Raised when input format not allowed"""
pass
class ReadMap1:
def __init__(self, input):
try:
if (type(input) == str) | (type(input) == int):
if type(input) == str:
self.input = input
else:
self.input = str(input)
else:
raise InputError
except InputError:
print("----Input not allowed----")
Output:
ReadMap1([1])
----Input not allowed----
<__main__.ReadMap1 object at 0x0000024B30B49FD0>
Many people confuse Python's __init__ with constructors from other languages, because like a constructor, __init__ is often used to initialize attributes. However, __init__ is not a constructor, but an initializer. The distinction is subtle though not terribly complicated in practice: The constructor constructs and returns an object, hence you can do my_obj = ClassConstructor() whereas the initializer only works on the object that the constructor already created. This is why Python's MyClass.__init__ returns nothing.
Your code is behaving as expected. You have told it:
if input is a string or integer, then do some stuff
if input is another type, then crash with InputError
if the above ever crashes with InputError, print an error message instead of crashing and move on
Then you pass the initializer [1] which is a Python list. This being neither a string nor a number, the interpreter raises an InputError. Normally exceptions cause a crash, but since you have the try catch, the crash is then negated, and instead a message is printed.
You can "fix" this trivially by changing your except clause to be like:
except InputError:
print("----Input not allowed----")
self.input = "default value"
It sounds like you want the initialization to be aborted in this case. Then you should remove the try-except, and let __init__ crash. The caller will then have to create the object like so:
try:
rm = ReadMap1([1])
except InputError:
print("----Input not allowed----")
Or you could omit the try-except here as well and allow the entire program to crash. It all depends: If input was invalid, could you program a way for your program to keep ticking, or is everything lost regardless?
There some additional issues in your code:
input is the name of a builtin function -- it's better to avoid shadowing it. Find a more unique name for your argument.
If you are going to check types in your if, you don't need try. You would either try to use the value and handle except TypeError, or you would use if type(..., not both.
It makes little sense to raise an exception inside a try that catches it. try is useful when the exception may arise form somewhere deep inside the code you are calling, and it would be impractical to directly check for the problem. In this case it is evident that you can check the type yourself, instead of relying on exceptions, so just do that.
If you are merely converting the value to a string, you don't need to check the type at all. Just do self.input = str(input). If it's already a string, Python will pass it unaltered. Everything else will be converted to a string (by calling the object's [or its parents'] .__str__ method).
You can define __new__() in order to intercept the creation of the object. You can return None here given a certain condition. Doing that will prevent __init__() from being called.
class ReadMap:
def __new__(cls, input):
if (type(input) == str) | (type(input) == int):
return super().__new__(cls)
print("----Input not allowed----")
return None
def __init__(self, input):
self.input = input
o = ReadMap(1)
o.input
# 1
p = ReadMap([1])
# prints ----Input not allowed----
print(p)
# None
I suspect this isn't a great idea, but I don't know how you are using this. It's pretty unexpected for the caller to call ReadMap() and get None, but it is possible. In general, I would opt for raising and letting the caller deal with the bad input — they after all sent the bad input.
In Python, I would like to check the type of the arguments passed to a function.
I wrote two implementations:
class FooFloat(float):
pass
# Solution 1
def foo(foo_instance):
if type(foo_instance) is FooFloat:
raise TypeError, 'foo only accept FooFloat input'
# Solution 2
def foo(foo_instance):
assert type(foo_instance) is FooFloat, 'foo only accept FooFloat input'
In my opinion the latter is easier to read and less boilerplate. However it will throw an AssertionError which is not the type of error I would like to raise.
Is there a better third solution in this case more common?
I was thinking about a decorator:
#argtype('foo_instance', FooFloat)
def foo(foo_instance):
pass
I like this idea and thinking of using it in future. I implement the third solution as following, please have a try.
def argtype(arg_name, arg_type):
def wrap_func(func):
def wrap_args(*args, **kwargs):
if not isinstance(kwargs.get(arg_name), arg_type):
raise TypeError, '%s\'s argument %s should be %s type' % (func.__name__, arg_name, arg_type.__name__)
return func(*args, **kwargs)
return wrap_args
return wrap_func
#argtype('bar', int)
#argtype('foo', int)
def work(foo, bar):
print 'hello word'
work(foo='a', bar=1)
Besides, I think use isinstance is more suitable if there is inheritance.
isinstance() does this. It accepts the type and subtypes.
if not isinstance(arg,<required type>):
raise TypeError("arg: expected `%s', got `%s'"%(<required type>,type(arg))
After eliminating all duplication (DRY principle), this becomes:
(n,t)=('arg',<required_type>);o=locals()[n]
if not isinstance(o,t):
raise TypeError("%(n)s: expected `%(t)s', got `%(rt)s'"
%dict(locals(),rt=type(o)) # fine in this particular case.
# See http://stackoverflow.com/a/26853961/648265
# for other ways and limitations
del n,t,o
Personally, I would use assert instead unless I care about which exceptions it throws (which I typically don't - an invalid argument is a fatal error, so I'm only interested in the fact one was thrown):
assert isinstance(arg,<type>),"expected `%s',got `%s'"%(<type>,type(arg))
#arg name would be seen in the source in stacktrace
Also consider duck typing instead of explicit type checks (this includes checking for special members, e.g. __iter__ for iterables). Full "duck typing vs type checks" discussion is beyond the scope of the current topic, but it looks like explicit checks are more fit for highly-specialized and/or complex interfaces as opposed to simple and generic ones.
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!')
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.