python属性函数中的健全性检查

python属性函数中的健全性检查,python,class,properties,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 lis

我是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。请记住,这并不是这个类的全部内容,我只是写下了与我的问题相关的内容,所以假设代码的行为与我描述的一样

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


那么,除了不使用列表之外,还有什么方法可以解决这个问题吗?或者我必须学会适应它吗?

Python和类型检查不能同时进行。学会接受它。这是使用代码传递正确类型的人的工作。记录代码期望的内容,但不要显式检查

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

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


替代类型检查的一个合理方法是转换为列表:

self.__teams = list(tms)
列表不兼容类型将导致在此行引发异常,从现在起,您可以确保正在处理列表。 (当然,这不会阻止有人将非字符串指定给列表。)


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

检查类似列表的集合(而不仅仅是狭隘的“列表或元组”)的正确方法是:

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

但那只是一个借口,不是解决你问题的正确方法。没有充分的理由不要进行类型检查。

此错误不是因为某些类型检查失败

除非您歪曲了您的代码(它显然是经过编辑的,因为您发布的内容无法正确运行),否则发生这种情况的原因是
match1。团队[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
>match1=match()
>>>匹配1.团队[0]=5
进门
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
TypeError:“tuple”对象不支持项分配
>>>match1.teams=[“team1”、“team2”]
成套
>>>匹配1.团队[0]=5
进门

属性
s的一个优点是能够进行数据验证——有时候,确保获得非常具体的内容非常重要

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

  • 将您的
    团队
    数据存储在无法修改的结构中,例如
    元组
    命名元组
    ;然后,当检索到数据时,无法对其进行更改

  • 让您的
    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)
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
当您在获取值后尝试赋值时,会发生以下情况:

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

['us', u'them']

如您所见,这些更改并没有使其返回到
匹配对象中。

如果您是对的,则使用get方法。所以彼得是对的,我运气不好。谢谢你们两位。谢谢Petr,你们可能是对的,这样做没有意义。如果类型未注册,则使用collections.Sequence将失败,并且由于ABC是可选的,所以肯定有很多不是。@EthanFurman:是的。检查ABCs比检查isinstance(…,(列表,元组))
要好,因为您至少可以将ABCs用作取悦执行检查的代码的拐杖。没有检查,当然更好。(为了记录在案,并非所有的ABC都是可选的–
isinstance(…,Sized)
对于任何带有
\uuu len\uuuuu
)错误的对象都是正确的——我的dbf类有一个
\uuu len\uuuu
方法,并且它的实例失败
isinstance(…,Sized)
。实际返回数据副本是一个好主意。你知道这会造成什么样的额外处理负载吗?@user977506:制作浅拷贝是次要的,但即使不是这样,你也不必担心,直到评测显示这是一个瓶颈。别忘了接受答案(点击你想要的复选标记)。
['us', u'them']