Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sanity checks in python properties function partially

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?

like image 521
tst Avatar asked Dec 22 '22 07:12

tst


2 Answers

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
like image 123
Nate Avatar answered Jan 13 '23 17:01

Nate


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.

like image 22
Petr Viktorin Avatar answered Jan 13 '23 17:01

Petr Viktorin