为什么要定义中间实例来调用Python中的方法?

时间:2013-06-24 02:26:30

标签: python oop

对不起这个新手问题的人,我相信答案很简单,但我仍然没有得到它。

尽管有一些阅读,面向对象编程仍然有黑暗我不明白。这是我第二次看到有必要创建一个类的中间实例来调用方法。请查看urllib doc:

中的示例
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)
你可以在这个阴影中带灯吗?非常感谢!

2 个答案:

答案 0 :(得分: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作为selfself.whateverx中隐藏的状态。

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

答案 1 :(得分:0)

urllib有许多不同类型的开启者。好吧,好吧,它有一对,而不是很多,但这不是重点。如果你按照你想要的方式调用urllib.open,那么urllib应该知道你打算使用哪个开启者?

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

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

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

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

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

因此,这个中间步骤的优点是告诉python您想要使用哪种常见API变体的便捷方法。所有知道如何使用此API的程序也将知道如何使用您的API版本(假设您没有违反标准API)。