sanity检查python属性部分功能

时间:2011-10-25 19:26:13

标签: python class properties

我是python的新手,我想要一些帮助。我创建了一些带有属性的类,以防止我传递无意义的参数。

所以例如我有这个类

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)

如果我写

match1=match()
match1.teams=(2,4)

我得到了一个例外,但是

match1.teams[0]=5

不会引发异常并传递数字5.请记住,这不是全部类,我只记下了与我的问题相关的内容,所以假设代码的行为和我描述的一样。 / p>

我想这是因为所有内容都是通过python中的引用传递的,但是我必须注意不要将无意义的数据分配给我的对象,否则就会失去首先拥有属性的目的。

那么,有没有办法解决这个问题,除了不使用列表,还是我必须学会使用它?

3 个答案:

答案 0 :(得分:5)

这个错误并不是因为你的某些类型检查失败了。

除非你歪曲了你的代码(显然已编辑,因为你发布的内容无法正常运行),这种情况正在发生,因为match1.teams[0]会调用getTeams函数,而不是setTeams功能。要亲自看看,请尝试以下练习:

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)

当我尝试这个时,我得到以下内容:

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

答案 1 :(得分:4)

Python和类型检查不会一起使用。学会与之共存。这是使用代码传递正确类型的人的工作。记录您的代码所期望的内容,但不要明确检查它。

除了列表和元组之外还有其他集合。你为什么要禁止,namedtuple? Python是一种动态语言,不要通过编写类型检查来对抗它。

在Python词汇表中查找EAFP。不要试图预测错误;当它们发生时处理它们。


你可以做的合理的事情而不是类型检查是转换为列表:

self.__teams = list(tms)

列表不兼容的类型将导致在此行引发异常,从现在开始,您可以确定您正在处理列表。 (当然,它不会阻止某人将非字符串分配给列表。)


哦,如果您(有充分理由!)需要进行类型检查,请使用isinstance函数,而不是比较type()。这也将捕获您所需类型的子类。而且,尝试使用最常用的基本类型。测试字符串(Unicode或其他)的正确方法是:

if isinstance(my_object, basestring):
    ....

检查列表式集合的正确方法 - 不仅仅是一个狭隘的“列表或元组” - 是:

import collections
if isinstance(my_object, collections.Sequence):
    ...

但这只是一个旁边,而不是你问题的正确解决方案。没有充分理由不要进行类型检查。

答案 2 :(得分:2)

property的一个优点是能够进行数据验证 - 有时确保获得非常具体的内容非常重要。

在您的情况下,您需要做以下两件事之一:

  • 将您的teams数据存储在无法修改的结构中,例如tuplenamedtuple;然后,当检索数据时,无法更改

  • 让您的get方法返回数据的副本,因此任何修改都不会弄乱您的原始

第一个解决方案(不可变类型)如下所示:

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)

当你在获得值后尝试分配时,会发生这种情况:

Traceback (most recent call last):
  File "test.py", line 22, in <module>
    match1.teams[0]=5
TypeError: 'tuple' object does not support item assignment

第二个解决方案(返回副本而不是原始副本)如下所示:

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

具有以下结果:

['us', u'them']

如您所见,更改未将其重新置于match对象中。