Python 2/3 : Why is type(Foo. __init__) 不同?

标签 python python-2.7 python-3.x types

考虑类:

Foo(object):
    def __init__(self):
        pass

在 Python 2 上执行 type(Foo.__init__):

Python 2.7.5 (default, Mar  9 2014, 22:15:05)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo(object):
...     def __init__(self):
...             pass
...
>>> type(Foo.__init__)
<type 'instancemethod'>

在 Python 3 上执行 type(Foo.__init__):

Python 3.4.1 (default, May 19 2014, 13:10:29)
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo(object):
...     def __init__(self):
...             pass
...
>>> type(Foo.__init__)
<class 'function'>

为什么 Python 2 和 3 上 type(Foo.__init__) 的返回结果不同?

最佳答案

如果您问更改了什么,简短版本位于 What's New in 3.0 :

The concept of “unbound methods” has been removed from the language. When referencing a method as a class attribute, you now get a plain function object.

更详细:

函数对象是 descriptor它的 __get__ 返回一个方法。在 Python 2.x 中,它看起来像这样(纯 Python 伪代码,并稍作简化):

class FunctionType(object):
    # other stuff
    def __get__(self, instance, cls):
        return MethodType(func=self, instance=instance, cls=cls)

class MethodType(object):
    def __init__(self, func, instance, cls):
        self.__func__, self.__instance__, self.__class__ = func, instance, cls
    def __call__(self, *args, **kwargs):
        if self.__self__ is not None:
            return self.__func__(self.__self__, *args, **kwargs)
        else:
            return self.__func__(*args, **kwargs)

因此,当您编写 Foo.__init__ 时,您将返回未绑定(bind)的方法 MethodType(__init__, None, Foo)

在 3.x 中,它看起来像这样:

class FunctionType(object):
    # other stuff
    def __get__(self, instance, cls):
        if instance is not None:
            return MethodType(func=self, instance=instance)
        else:
            return self

class MethodType(object):
    def __init__(self, func, instance):
        self.__func__, self.__instance__, self.__class__ = func, instance, type(instance)
    def __call__(self, *args, **kwargs):
        return self.__func__(self.__self__, *args, **kwargs)

有关完整详细信息,请参阅 3.x 的引用文档中的标准层次结构与 2.x ,在“可调用类型”下。

<小时/>

如果你问为什么它被改变了......好吧,据我记得,对此没有太多讨论 - 没有 PEP,没有关于 python-dev 或 -ideas 的长讨论等等——文档中只给出了这一行。

但推理似乎很明显。未绑定(bind)的方法并不是特别有用;它们只是额外的脚手架,提供与它们所包装的函数完全相同的行为。*它们仅在 2.2-2.7 中出现,以便更好地模拟经典类的行为**和/或因为这种方式似乎更容易实现和/或因为在设计初期,不清楚是否可以在没有它的情况下实现像 @classmethod 这样的东西,当 Guido 足够深入地意识到那里没有问题时,它是更容易将设计保留为他最初编写的样子。

<小时/>

* 绑定(bind)方法的 CPython 实现添加了一个 isinstance 检查来验证第一个参数是否是 self.__class__,但这没有明确记录,没有人编写依赖于它的代码,并且它对调试的帮助不如您所期望的那样。

** 如果你想知道为什么经典类会这样工作,你必须深入研究 Guido 的 Python History博客——如果您有时间,所有这些都值得一读。

关于Python 2/3 : Why is type(Foo. __init__) 不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25228778/

相关文章:

python - 使用 Python 获取 Wikipedia 文章

python - pocketsphinx python 给出错误的时间戳

python - IIS 7.5 上的 Mercurial 和 hgweb - python 错误

Python2 : Resize image plotted using Matplotlib

python - 在 Python 上解码 HTML 实体

python - Python 3中带有re.sub的lambda函数

python - 重命名未堆叠数据透视表中的 pandas 列值

python curses 终端设置已更改

Python Snakefood 模块导入普遍失败

python - 我应该导入哪个库以在 Python 中使用 create shortcut()?