迭代类对象

时间:2011-06-20 03:15:53

标签: python range

这不是一个真实的世界计划,但我想知道为什么不能这样做。

我正在考虑numpy.r_对象,并尝试做类似的事情,但只是创建一个类而没有实例化

整数的简单代码(有一些缺陷)可能是:

class r_:
    @classmethod
    def __getitem__(clc, sl):
        try:
            return range(sl)
        except TypeError:
            sl = sl.start, sl.stop, sl.step
            return range(*(i for i in sl if i is not None))

但是当我尝试r_[1:10]时,我会收到TypeError: 'type' object is not subscriptable

当然,代码与r_.__getitem__(slice(1,10))一起使用,但这不是我想要的。

在这种情况下,我可以做些什么,而不是使用r_()[1:10]

4 个答案:

答案 0 :(得分:3)

解析obj[index]的协议是直接在__getitem__类型中查找obj方法在obj上查找方法(如果obj没有名称为__getitem__的实例属性,通常会回退到查找类型的方法。

这很容易验证。

>>> class Foo(object):
    pass

>>> def __getitem__(self, index):
    return index

>>> f = Foo()
>>> f.__getitem__ = __getitem__
>>> f[3]
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    f[3]
TypeError: 'Foo' object does not support indexing
>>> Foo.__getitem__ = __getitem__
>>> f[3]
3

我不知道为什么它会以这种方式运作,但我猜想至少部分原因正是为了防止你想要做的事情;如果每个定义__getitem__的类使其实例可转换,意外地获得了自己索引的能力,那将是令人惊讶的。在绝大多数情况下,尝试索引类的代码将是一个错误,因此如果__getitem__方法碰巧能够返回某些内容,那么如果没有被捕获则会很糟糕。

为什么不直接将该类调用为其他类,并将其实例绑定到名称r_?然后你就可以r_[1:10]

答案 1 :(得分:2)

您尝试执行的操作类似list[1:5]set[1:5] =)特殊__getitem__方法仅适用于实例。

通常做的只是创建类的单个(“单例”)实例:

class r_class(object):
    ...

r_ = r_class()

现在你可以做到:

r_[1:5]

您也可以使用元类,但这可能不仅仅是必要的。

  

不,我的问题是关于课程中的 getitem ,而不是实例

然后你确实需要元类。

class r_meta(type):
    def __getitem__(cls, key):
        return range(key)
class r_(object, metaclass=r_meta):
    pass

演示:

>>> r_[5]
range(0, 5)

如果您传入r_[1:5],您将获得slice个对象。请help(slice)获取更多信息;您可以访问key.stop if isinstance(key,slice) else key等值。

答案 2 :(得分:1)

__getitem__()定义为r_的{​​{3}}中的常规方法。

答案 3 :(得分:1)

这种行为的原因在于如何查找__getitem__()之类的特殊方法。

首先在对象__dict__中查找属性,如果在那里找不到,则在类__dict__中查找属性。这就是为什么,例如这有效:

>>> class Test1(object):
...     x = 'hello'
...
>>> t = Test1()
>>> t.__dict__
{}
>>> t.x
'hello'

类主体中定义的方法存储在类__dict__中:

>>> class Test2(object):
...     def foo(self):
...         print 'hello'
...
>>> t = Test2()
>>> t.foo()
hello
>>> Test2.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with Test2 instance as first argument (got nothing
instead)

到目前为止,这并不奇怪。然而,当谈到特殊方法时,Python的行为有点(或非常)不同:

>>> class Test3(object):
...     def __getitem__(self, key):
...         return 1
...
>>> t = Test3()
>>> t.__getitem__('a key')
1
>>> Test3['a key']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'type' object is unsubscriptable

错误消息非常不同。使用Test2,Python会抱怨一个未绑定的方法调用,而使用Test3则会抱怨unsubscriptability。

如果你尝试调用一个特殊的方法 - 通过在对象上使用它的关联运算符 - ,Python不会尝试在对象__dict__中找到它,而是直接到对象类__dict__ ,如果对象本身是一个类,是一个元类。所以你必须在那里定义它:

>>> class Test4(object):
...     class __metaclass__(type):
...         def __getitem__(cls, key):
...             return 1
...
>>> Test4['a key']
1

别无他法。引用PEP20应该有一个 - 最好只有一个 - 显而易见的方法。