python:方法取决于param是int还是string

时间:2011-09-23 20:10:09

标签: python

是否有更多pythonic方式来编写__getitem__而不是以下内容?问题是检查类型并根据调用中参数的类型执行不同的操作。

class This():
    def __init__(self, name, value):
        self.name, self.value = name, value

class That():
    def __init__(self):
        self.this_list = []
    def add_this(self, this):
        self.this_list.append(this)
    def __getitem__(self, x):
        if isinstance(x, int):
            return self.this_list[x] # could wrap in try/except for error checking
        elif isinstance(x, str):
            for this in self.this_list:
                if this.name == x:
                    return this
            return None


a = This('a', 1)
b = This('b', 2)
c = That()
c.add_this(a)
c.add_this(b)
print c[1].name
print c['a'].name

4 个答案:

答案 0 :(得分:2)

有很多选择,但我认为没有一个最佳选择。这取决于您的使用案例和偏好。只是给你一些提示:

您真的必须将数据存储在列表中吗?在您的示例中,您可以使用字典并插入对象两次:一次使用整数作为键,一次使用字符串作为键。这会让你的__getitem__变得非常简单。 ; - )

另一种选择是使您的界面更加明确,并使用byInt / byString方法。你当然应该选择更好的名字。

如果你提供更多关于你真正想做的事情的细节,我可以提出更多的选择。

答案 1 :(得分:2)

您几乎总是更好地测试您想要的项目的行为,而不是明确地测试类型。在您的情况下,我只是尝试通过索引首先获取所需的项目并捕获TypeError以按名称进行检查。

def __getitem__(self, key):
    try:
        return self.this_list[key]
    except TypeError:
        try:
            return next(item for item in self.this_list if item.name == key)
        except StopIteration:
            raise KeyError("key `%s` not found" % key)

请注意,这也会自动使用切片,因为在这种情况下,键将是切片对象,并且可以使用[...]表示法正常工作。

您应该在类中使用dict而不是list,而不是在列表中搜索对象属性。例外情况是,如果你真的需要切片,或者你的班级以外的代码可以改变名称。

另一种(可能是非常规的)可能性是在__eq__()类上实现特殊方法This,允许将其与字符串进行比较,以便在类的name属性时是(说)“杰里”,然后是This("Jerry", 0) == "Jerry"。那么你实际上并不需要容器类,只能使用常规列表:

class This(object):
    def __init__(self, name, value):
        self.name, self.value = name, value
    def __eq__(self, other):
        return self.name == other

thislist = [This("Jerry", 42), This("Amy", 36)]

"Jerry" in thislist       # True
thislist.index("Amy")     # 1

按名称访问项目的语法仍然有点毛茸茸:

thislist[thislist.index("Amy")]

但你可以简单地将list子类化,并将其与我之前的建议相结合,后者变得更简单,更通用,因为它适用于任何知道如何将自身与您正在使用的任何类型的键进行比较的对象:

class That(list):
    def __getitem__(self, key):
        try:
            return list.__getitem__(self, key)
        except TypeError:
            return list.__getitem__(self, self.index(key))

thislist = That([This("Jerry", 42), This("Amy", 36)])
thislist["Amy"].value       # 36

答案 2 :(得分:1)

您可以定义两个私有方法__getitem_int()__getitem_str()。然后,您可以使用getattr()来根据type(x).__name__处理正确的方法并调用特定于类型的方法。

了解如何在深入了解parsing xml示例中实现KantGenerator.parse()

答案 3 :(得分:1)

  

下面是否有更多pythonic方法来编写getitem?

只是轻微的。两个序列使用__getitem__,其中使用intslice,以及通过映射,几乎可以使用任何内容。看起来您正在实现序列类型和映射类型接口,因此您仍然坚持使用检查类型。

遗漏了两件事:

  • 支持切片(但只有在你希望你的支持时才将其放入)
  • 引发失败异常(在这种情况下返回None不是pythonic)

以下是更新后的__getitem__

def __getitem__(self, x):
    if isinstance(x, int):
        return self.this_list[x]
    elif isinstance(x, slice):
        return self.this_list[slice]
    elif isinstance(x, str):
        for this in self.this_list:
            if this.name == x:
                return this
        return None
    raise KeyError("invalid key: %r" % x)

此时你有两个可能的异常

  • IndexError(如果x超出this_list的范围)
  • KeyError(如果找不到名称,或传递了strint以外的内容)

这对您来说可能没问题,或者您可能想要创建一个在所有情况下都会返回的自定义异常:

class LookupError(Exception):
    "x is neither int nor str, or no matching This instance found"

这是更新的代码(Python 2.x):

class LookupError(IndexError, KeyError):
    "x is neither int nor str, or no matching This instance found"

class This():
    def __init__(self, name, value):
        self.name, self.value = name, value

class That(object):
    def __init__(self):
        self.this_list = []
    def add_this(self, this):
        self.this_list.append(this)
    def __getitem__(self, x):
        try:
            if isinstance(x, int):
                return self.this_list[x]
            elif isinstance(x, slice):
                return self.this_list[slice]
            elif isinstance(x, str):
                for this in self.this_list:
                    if this.name == x:
                        return this
            raise KeyError("invalid key: %r" % x)
        except (IndexError, KeyError), err:
            raise LookupError(err.message)


a = This('a', 1)
b = This('b', 2)
c =  That()
c.add_this(a)
c.add_this(b)
print c[1].name
print c['a'].name
try:
    print c[2.0]
except LookupError, e:
    print e
try:
    print c['c']
except LookupError, e:
    print e