0

对不起这个新手问题,我相信答案很简单,但我仍然不明白。

尽管进行了一些阅读,但面向对象编程仍然有我不理解的黑暗。这是我第二次看到需要创建类的中间实例来调用方法。urllib从文档中检查以下示例:

import urllib
opener = urllib.FancyURLopener({})
f = opener.open("http://www.python.org/")
f.read()

我不明白为什么它需要创建opener实例来调用该open方法。我不明白为什么以下代码不起作用:

import urllib
f = urllib.FancyURLopener.open("http://www.python.org/")
f.read()

尝试调用时出现错误urllib.FancyURLopener.open

TypeError: unbound method open() must be called with FancyURLopener instance as first argument (got str instance instead)

你们能在这个阴影中带灯吗?非常感谢 !

4

2 回答 2

1

您不是在此处创建该类的“中间实例”,而是在创建该类的一个实例(如果您愿意,可以将其称为“主”实例)。

这里,FancyURLopener是类本身的名称。该类是一个类型

>>> import urllib
>>> type(urllib.FancyURLopener)
<type 'classobj'>

(事实上​​它是一个<type 'classobj'>而不是<type 'type'>告诉你这是 Python2 的“旧式”类之一,但这完全是另一个问题的主题:-))。因为它是一种类型,所以要实际使用它,您通常必须创建该类型的对象。有点类似于:要使用一些整数,你必须制作整数,而不仅仅是指int类型。

类类型具有(即,让您引用)一堆“类方法”,它们实际上只是“采用状态持有者变量的函数”,我们通常拼写为self. 所以你创建了一个类型的实例:

>>> x = urllib.FancyURLopener()

现在您可以调用x.open(...)真正意味着的函数:urllib.FancyURLopener.open(x, ...). 这使得里面的代码可以在里面urllib.FancyURLopener存储各种状态x。想让 HTTP 连接使用持久性?在内部保留一些x关于连接是否仍然打开的状态。想要操作一些网络服务器 cookie?在内部保留一些状态x以帮助追踪它们。任何你(urllib 的作者)想要的东西,你都可以把它塞进x. 当您的函数被调用时,您将调用者x作为您的self, 并且self.whatever是隐藏在 中的状态x

(Python 实际上将所有这些所谓的隐藏状态暴露给调用者,因此人们使用_spam__eggs成员名称来帮助它们不碍事,但这又远离了简单的“为什么”。)

于 2013-06-24T02:55:49.527 回答
0

urllib 有许多不同类型的开瓶器。好吧,它有几个,不是很多,但这不是重点。如果您urllib.open以您想要的方式调用,urllib 应该如何知道您打算使用哪个开启程序?

它知道这一点,因为您的“中间步骤”创建了一个特定类型的开瓶器的实例。如果您创建 的实例FancyURLOpener,python 将知道这是您想要的特定类型的开启器。如果要URLOpener改用,请创建that的实例,然后可以改用它。

不要将此视为中间步骤,将其视为步骤-告诉python“这是我要使用的特定类型的url opener”的方式。

现在,当然,python 可以以非 OO 方式编写,并让您告诉 open 方法您想使用哪种类型的开启器。例如,非 OO 方式可能会让您这样做:f = urllib.open({}, type="Fancy"). 那可以工作,对吧?许多人以这种风格编写代码并且它可以工作,那么为什么要为对象烦恼呢?

使用面向对象的方法意味着您可以使用自己的特殊属性创建自己的开启程序。例如,花哨版本的文档说它“[提供]对以下 HTTP 响应代码的默认处理:301、302、303、307 和 401”。如果您还想要默认处理 404 怎么办?您可以将其称为 CJOpener,但为了使用它,您必须修改urllib自身以将“CJ”和“Fancy”作为最后一个参数。你真的想修改内置的python代码吗?

类和对象是解决方案。该类定义了接口——开启者应该工作的标准方式。您可以自由地创建自己的变体,您所要做的就是创建一个子类,然后创建该子类的一个实例。一旦你这样做了,世界上任何地方的任何 Python 代码都知道如何使用FancyURLOpenerURLOpener立即知道如何使用你的开瓶器。

所以,这个中间步骤的好处是可以方便地告诉 python 你想使用哪个通用 API 的变体。所有知道如何使用此 API 的程序也将知道如何使用您的 API 版本(假设您没有破坏标准 API)。

于 2013-06-24T02:41:54.390 回答