我知道Python中至少有3种方法具有不同的第一个参数:
self
cls
这些经典方法在下面的Test
类中实现,其中包括一个常用方法:
class Test():
def __init__(self):
pass
def instance_mthd(self):
print("Instance method.")
@classmethod
def class_mthd(cls):
print("Class method.")
@staticmethod
def static_mthd():
print("Static method.")
def unknown_mthd():
# No decoration --> instance method, but
# No self (or cls) --> static method, so ... (?)
print("Unknown method.")
在Python3中,unknown_mthd
可以安全地调用,但在Python2中它会引发一个错误:
这个错误表明python2不打算使用这种方法。也许它现在的允许是由于Python3(REF 001)中取消了未绑定的方法。而且,unknown_mthd
不接受arg,它可以被一个类(如staticmethod,Test.unknown_mthd()
)绑定到。但是,它不是显式的staticmethod(没有decorator)。在
问题
unknown_mthd
是什么类型的方法?在unknown_mthd
?在一些初步检查结果不确定:
>>> # Types
>>> print("i", type(t.instance_mthd))
>>> print("c", type(Test.class_mthd))
>>> print("s", type(t.static_mthd))
>>> print("u", type(Test.unknown_mthd))
>>> print()
>>> # __dict__ Types, REF 002
>>> print("i", type(t.__class__.__dict__["instance_mthd"]))
>>> print("c", type(t.__class__.__dict__["class_mthd"]))
>>> print("s", type(t.__class__.__dict__["static_mthd"]))
>>> print("u", type(t.__class__.__dict__["unknown_mthd"]))
>>> print()
i <class 'method'>
c <class 'method'>
s <class 'function'>
u <class 'function'>
i <class 'function'>
c <class 'classmethod'>
s <class 'staticmethod'>
u <class 'function'>
第一组类型检查表明unknown_mthd
与静态方法类似。第二个建议它类似于实例方法。我不知道这个方法是什么,也不知道为什么要用它来代替经典的方法。如果您能给我一些关于如何更好地检查和理解它的建议,我将不胜感激。谢谢。在
@BrenBarn回答你的问题做得很好。然而,这个答案增加了很多细节:
首先,绑定和未绑定方法的这种更改是特定于版本的,它与新样式或经典类无关:
默认为2.X经典类
默认为3.X个新样式类
^{pr2}$你已经在你的问题中提到了这一点,作为提醒,提两次也没什么坏处。在
unknown_meth
不接受参数,通常是因为您定义了没有参数的函数,因此它不接受任何参数。请小心,当您通过类名引用静态方法以及编码的unknown_meth
方法时,它们不会神奇地绑定到类(例如,Test.unknown_meth
)。在python3.X下,Test.unknow_meth
返回3.X中的简单函数对象,而不是绑定到类的方法。在我不能代表CPython开发人员说话,也不能说我是他们的代表,但从我作为Python程序员的经验来看,他们似乎想摆脱一个糟糕的限制,尤其是考虑到Python是一种非常动态的,而不是一种限制性语言;为什么要测试传递给类方法的对象类型和因此,将方法限制为类的特定实例?类型测试消除了多态性。如果你只是返回一个简单的函数,当一个方法通过一个函数行为类似于静态方法的类获取时,你可以认为
unknown_meth
是3.X下的静态方法,只要你小心不要通过Test
的实例来获取它,你就可以走了。在3.X以下:
^{4}$正如你所见,它只是3.X中的一个函数。在2.X下继续上一个会话:
unknown_mthd
是一个存在于Test__dict__
中的简单函数,实际上只是一个存在于Test
名称空间字典中的简单函数。那么,它何时成为MethodType
的实例?好吧,当您从返回未绑定方法的类本身或返回绑定方法的实例获取method属性时,它将成为MethodType
的实例。在3.X中,Test.unknown_mthd
是FunctionType
的一个简单函数实例,Test().unknown_mthd
是{Test
的原始实例,并将其作为函数调用的第一个参数隐式添加。在同样,因为
Test.unknown_mthd
只是3.X下的一个简单函数,而在2.X中,unknown_mthd
不是简单函数,必须在调用时传递Test
的实例。在是的。您提到的三种内置类型(实例方法、类方法、静态方法),如果计算
@property
,则有四种,任何人都可以定义新的方法类型。在一旦您理解了这样做的机制,就很容易解释为什么在python3中可以从类调用
unknown_mthd
。在一种新的方法
假设我们想创建一个新的方法类型,将其称为
optionalselfmethod
,这样我们就可以这样做:用法如下:
^{pr2}$当对实例调用时,
optionalselfmethod
的工作方式与普通的实例方法类似,但在类上调用时,它总是接收第一个参数的None
。如果它是一个普通的实例方法,则必须始终为self
参数传递一个显式值才能使其工作。在这是怎么回事?你怎么能像这样创建一个新的类型?在
描述符协议
当Python查找一个实例的字段时,即当您查找
x.whatever
时,它会在多个位置进行检查。当然,它会检查实例的__dict__
,但它也会检查对象类的__dict__
及其基类。在dict实例中,Python只是在寻找值,所以如果x.__dict__['whatever']
存在,那么就是这个值。但是,在dict类中,Python正在寻找实现the Descriptor Protocol的对象。在描述符协议是三种内置方法的工作方式,它是
@property
的工作方式,也是我们的特殊optionalselfmethod
的工作方式。在基本上,如果dict类具有正确的名称1,Python会检查它是否有一个
__get__
方法,并像type(x).whatever.__get__(x, type(x))
一样调用它,那么从__get__
返回的值将用作字段值。在例如,一个通常返回3:
用法如下:
^{4}$请注意,使用实例和类类型调用描述符。它也可以用于类:
{13>仍然是一个描述符,而不是类的自变量。在
这允许方法的简单实现:函数只是实现描述符协议。当您对函数调用
__get__
时,它返回实例的绑定方法。如果实例是None
,则返回原始函数。您可以亲自致电__get__
来查看:@classmethod
和{__get__
方法创建代理对象。类方法的__get__
将方法绑定到实例,而静态方法的__get__
不绑定到任何东西,即使在实例上调用也是如此。在可选的Self方法实现
我们可以做一些类似的事情来创建一个新的方法,这个方法可以选择性地绑定到一个实例上。在
当您用
optionalselfmethod
修饰函数时,该函数将替换为我们的代理。此代理保存原始方法并提供一个__get__
方法,该方法返回boudnoptionalselfmethod
。当我们创建boundoptionalselfmethod
时,我们告诉它要调用的函数和作为self
传递的值。最后,调用boundoptionalselfmethod
调用原始函数,但要在第一个参数中插入实例或None
。在具体问题
我相信这是有意的;但是目的本来是为了消除无约束的方法。在python2和python3中,})。在
def
always都会创建一个函数(通过检查类型的__dict__
可以看到这一点:即使Test.instance_mthd
返回为<unbound method Test.instance_mthd>
,Test.__dict__['instance_mthd']
仍然是{在Python2中,
function
的__get__
方法总是返回一个instancemethod
,即使是通过类访问的时候。当通过实例访问时,方法绑定到该实例。当通过类访问时,该方法是非绑定的,并且包含一个检查tha的机制第一个参数是正确类的实例。在在Python3中,
function
的__get__
方法在通过类访问时返回原始函数,在通过实例访问时返回method
。在我不知道确切的原理,但我想对类级函数的第一个参数进行类型检查被认为是不必要的,甚至是有害的;Python毕竟允许duck类型。在
unknown_mthd
是一个普通函数,就像任何普通的实例方法一样。它只在通过实例调用时失败,因为当method.__call__
试图用绑定实例调用function
unknown_mthd
时,它没有接受足够的参数来接收instance
参数。在因为它只是一个普通的
function
,和其他的function
一样。当用作实例方法时,我没有足够的参数来正确工作。在您可能会注意到,}无论是通过实例还是通过类调用它们的工作方式都是相同的,而{}只有在通过类调用时才能正常工作,而在通过实例调用时失败。在
classmethod
和{1。如果一个特定的名称在实例dict中有一个值,并且在dict类中有一个描述符,那么使用哪一个取决于它是什么样的描述符。如果描述符只定义},因此即使它们是只读的,也永远不会从实例
__get__
,则使用实例dict中的值。如果描述符也定义了__set__
,那么它就是一个数据描述符,描述符总是获胜。这就是为什么您可以在一个方法之上而不是@property
;方法只定义__get__
,这样你就可以把东西放在实例dict中的同一个命名槽中,而@properties
定义{__dict__
中获得值,即使您以前绕过了属性查找并粘贴了一个值例如x.__dict__['whatever'] = 3
。相关问题 更多 >
编程相关推荐