type(1)是否等于type .__ call __(1)?

时间:2018-09-19 03:38:06

标签: python python-2.7

我在Jupyter控制台(Python的版本为2.7.14)中运行代码,

type(1)
Out[71]: int

type.__call__(1)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-72-0ed097e2c413> in <module>()
----> 1 type.__call__(1)

TypeError: descriptor '__call__' requires a 'type' object but received a 'int'

我猜type(...)等效于type.__call__(...),但似乎没有。所以我想知道:

  1. 当我调用type(1)type(name, base, dict)时,python解释器做了什么?
  2. 何时调用type.__call__(...)

谢谢。

4 个答案:

答案 0 :(得分:5)

简短版本:type.__call__有两种合理的解决方法:绑定方法表示用于调用type本身的方法,或者未绑定方法表示用于调用{{ 1}}。您期待第一个结果,但是实际得到的却是第二个。


长版:

type通常等效于some_callable(thing)。例如,some_callable.__call__(thing)等效于print(1)(只要您打开了print.__call__(1)):

from __future__ import print_function

>>> print(1) 1 >>> print.__call__(1) 1 是可调用的,并且type 等效于type(thing),只是属性查找会变得很复杂。

type.__call__(thing)属性查找期间,Python在type.__call__及其超类(仅type)中搜索键为object的{​​{1}}条目。 Python 搜索__dict__的类型和'__call__'的类型的超类来查找这样的type条目。 (您可以在负责处理类型属性查找的C函数type_getattro中看到负责调用这些搜索的代码。)由于type是其自身的类型,因此这两个搜索都找到了__dict__

这些搜索之一优先。做出选择的方式以及选择甚至很重要的原因是descriptor protocol。如果第一次搜索获胜,则照常使用描述符协议来查找类属性(type);如果第二次搜索获胜,则按常规应用描述符协议来查找实例属性(type.__dict__['__call__'])。第二个搜索需要赢得descriptor.__get__(None, cls)才能像descriptor.__get__(instance, cls)一样进行,但只有type.__call__(thing)数据描述符时才可以,否则它不会获胜。

第一个搜索获胜。通常使用描述符协议来查找类属性,而查找type(thing)的结果是 unbound 方法。它代表type.__dict__['__call__']实例的常规type.__call__方法,而不是__call__作为实例类型的type实例方法。它需要被称为

__call__

等同于type


毕竟,您可能想知道type.__call__(type, 1) 的工作方式而不会遇到所有这些麻烦。就语言语义而言,Python仅在查找type(1)的{​​{1}}方法时才执行第二次搜索,因此第一次搜索不会成功,因为它甚至没有发生。在实际实现方面,CPython根本不查找type(thing)方法;它查找type的类型,转到__call__类型的与__call__对应的C级插槽,并调用the function it finds。对于使用Python实现的type方法,C插槽将包含一个查找并调用Python方法的函数,但是对于使用C实现的type,C插槽将包含__call__直接实施。

答案 1 :(得分:4)

由于type是一个类,因此需要其__new__方法:

>>> type.__new__(type, 1)
<type 'int'>

通常,cls(*args, **kwargs)等效于cls.__new__(cls, *args, **kwargs),然后在返回的对象上调用.__init__(*args, **kwargs)

这是type()的这种行为的一种可能的实现方式:

class MyType(object):
    def __new__(cls, obj):
        return obj.__class__

    def __init__(self, hahaha):
        pass

有关问题2,请参见blhsing's answer

答案 2 :(得分:2)

要回答问题的后半部分,因为@iBug已经回答了前半部分,所以type.__call__type的一种方法,因此应将其称为type.__call__(1)而不是self = type(1) self.__call__(1) 绑定方法:

type.__call__(type(1), 1)

或:

type(1).__new__(type(1), 1)

两者均返回:

int(1)

实际上是type(1),因为int返回public class ChangesTracker : IDisposable { private Word.UndoRecord undoRecord; public ChangesTracker(string name) { Globals.ThisAddIn.Application.ScreenUpdating = false; undoRecord = Globals.ThisAddIn.Application.UndoRecord; undoRecord.StartCustomRecord(name); } protected virtual void Dispose(bool disposing) { if (disposing) { if (undoRecord != null) { undoRecord.EndCustomRecord( ); Globals.ThisAddIn.Application.ScreenUpdating = true; undoRecord = null; } } } public void Dispose() { Dispose(true); } ~ChangesTracker() { Dispose(false); } }

答案 3 :(得分:1)

正如@ user2357112所说,原因是type既充当内置函数(也可以作为元类加倍,但在这里并不重要)

type(1)(根据Built-in Functions — Python 2.7.15 documentation)用作返回对象类型或创建新的自定义类型对象的常规函数​​。

In [13]: type?
Docstring:
type(object) -> the object's type
type(name, bases, dict) -> a new type
Type:      type
另一方面,

type.__call__是对type类(即类型)的对象的调用的实现,如您所知,它创建该类型的实例。即一个未绑定的方法:

In [14]: type.__call__?
Type:        wrapper_descriptor
String form: <slot wrapper '__call__' of 'type' objects>
Namespace:   Python builtin
Docstring:   x.__call__(...) <==> x(...)

执行路径不同的原因是在第一种情况下没有完成属性查找:

In [16]: dis.dis(compile("type(1)","<stdin>","eval"))
  1           0 LOAD_NAME                0 (type)
              3 LOAD_CONST               0 (1)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE

In [17]: dis.dis(compile("type.__call__(1)","<stdin>","eval"))
  1           0 LOAD_NAME                0 (type)
              3 LOAD_ATTR                1 (__call__)
              6 LOAD_CONST               0 (1)
              9 CALL_FUNCTION            1
             12 RETURN_VALUE

还有{{3}}。