我在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__(...)
,但似乎没有。所以我想知道:
type(1)
或type(name, base, dict)
时,python解释器做了什么?type.__call__(...)
?谢谢。
答案 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(...)
typeobject.c:slot_tp_call()
实现,typeobject.c:slotdefs[]
在__call__
属性查询中通过when calling an object like a function, tp_call
takes priority返回,其内容type.__dict__
在初始化期间被填充。执行路径不同的原因是在第一种情况下没有完成属性查找:
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}}。