6

Core Python Programming一书中关于该主题的示例Delegation似乎不起作用..或者我可能没有清楚地理解该主题..

下面是代码,其中类CapOpen包装了一个对象并定义了在模式下打开时file的修改行为。它应该只以大写形式写入所有字符串。filewrite

但是,当我尝试打开文件进行读取并对其进行迭代以打印每一行时,出现以下异常:

Traceback (most recent call last):
  File "D:/_Python Practice/Core Python Programming/chapter_13_Classes/
        WrappingFileObject.py", line 29, in <module>
    for each_line in f:
TypeError: 'CapOpen' object is not iterable

这很奇怪,因为虽然我没有明确定义迭代器方法,但我希望调用通过委托__getattr__给底层file对象。这是代码。我错过了什么吗?

class CapOpen(object):
    def __init__(self, filename, mode='r', buf=-1):
        self.file = open(filename, mode, buf)

    def __str__(self):
        return str(self.file)

    def __repr__(self):
        return `self.file`

    def write(self, line):
        self.file.write(line.upper())

    def __getattr__(self, attr):
        return getattr(self.file, attr)


f = CapOpen('wrappingfile.txt', 'w')
f.write('delegation example\n')
f.write('faye is good\n')
f.write('at delegating\n')
f.close()

f = CapOpen('wrappingfile.txt', 'r')

for each_line in f:   # I am getting Exception Here..
    print each_line,

我正在使用 Python 2.7。

4

2 回答 2

13

这是新式类的 Python 实现决策的非直观结果:

除了为了正确性而绕过任何实例属性之外,隐式特殊方法查找通常也会绕过__getattribute__()对象元类的方法......

以这种方式绕过__getattribute__()机制为解释器中的速度优化提供了很大的空间,但代价是处理特殊方法的一些灵活性(特殊方法必须在类对象本身上设置,以便解释器始终调用) .

__getattr__在/__getattribute__的文档中也明确指出了这一点:

注意 当通过语言语法或内置函数隐式调用查找特殊方法时,仍可能绕过此方法。请参阅新式类的特殊方法查找。

换句话说,__getattr__当您的属性未定义时,您不能依赖总是拦截您的方法查找。这并不直观,因为期望这些隐式查找遵循与访问您的对象的所有其他客户端相同的路径是合理的。如果您f.__iter__直接从其他代码调用,它将按预期解决。但是,当直接从该语言调用时,情况并非如此。

您引用的书很旧,因此原始示例可能使用了旧式类。如果您从 中删除继承object,您的代码将按预期工作。话虽如此,您应该避免编写旧样式的类,因为它们将在 Python 3 中过时。如果您愿意,您仍然可以通过实现__iter__并立即委托给底层的self.file.__iter__.

file或者,直接从对象继承,并且__iter__可以通过正常查找获得,这样也可以。

于 2012-09-26T06:47:59.167 回答
2

一个对象是可迭代的,它的类必须具有__iter____getitem__定义。

__getattr__仅在从实例中检索某些内容时调用,但由于支持迭代的方式有多种,Python 会首先查看是否存在适当的方法。

试试这个:

class Fake(object):
    def __getattr__(self, name):
        print "Nope, no %s here!" % name
        raise AttributeError

f = Fake()
for not_here in f:
    print not_here

如您所见,引发了相同的错误:TypeError: 'Fake' object is not iterable.

如果你这样做:

print '__getattr__' in Fake.__dict__
print '__iter__' in Fake.__dict__
print '__getitem__' in Fake.__dict__

您可以看到 Python 所看到的:既不存在__iter__也不__getitem__存在,因此 Python 不知道如何迭代它。虽然 Python可以尝试然后捕获异常,但我怀疑它不捕获异常的原因是捕获异常的速度相当慢。

有关制作迭代器的多种方法,请参见我的答案。

于 2012-09-26T07:53:04.830 回答