Are there more than three types of methods in Python?
我知道Python中至少有3种方法具有不同的第一个参数:
这些经典方法在下面的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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.") |
在python 3中,可以安全地调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | >>> t = Test() >>> # Python 3 >>> t.instance_mthd() >>> Test.class_mthd() >>> t.static_mthd() >>> Test.unknown_mthd() Instance method. Class method. Static method. Unknown method. >>> # Python 2 >>> Test.unknown_mthd() TypeError: unbound method unknown_mthd() must be called with Test instance as first argument (got nothing instead) |
此错误表明这样的方法不适用于Python2。也许它现在的允许是由于在python 3中消除了未绑定的方法(ref 001)。此外,绑定到。但是,它不是一个显式的静态方法(没有修饰符)。
问题
一些初步检查得出了不确定的结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | >>> # 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'> |
第一组类型检查表明
- 参考001:python 3中的新功能:"未绑定方法"已被删除
- 参考002:如何在python 3中区分实例方法、类方法、静态方法或函数?
- 参考文件003:python中@staticmethod的意义是什么?
一些背景:在Python2中,"常规"实例方法可能会产生两种方法对象,这取决于您是通过实例还是通过类访问它们。如果您执行了
在python 3中,未绑定的方法被删除。现在执行
Was making a method this way intentional in Python 3's design?
如果你的意思是,有意删除未绑定的方法,答案是肯定的。您可以在邮件列表中看到guido的讨论。基本上,决定了无绑定方法会增加复杂性,但收益很小。
Among the classic method types, what type of method is unknown_mthd?
它是一个实例方法,但已损坏。当您访问它时,会创建一个绑定方法对象,但是由于它不接受任何参数,所以它无法接受
Why can unknown_mthd be called by the class without passing an argument?
在python 3中,未绑定的方法被删除,所以
Are there more than three types of methods in Python?
Ok.
对。您提到的内置类型有三种(实例方法、类方法、静态方法),如果计算
一旦您了解了这样做的机制,就很容易解释为什么可以从Python3的类中调用
假设我们想要创建一种新的方法,称之为
1 2 3 4 | class Test(object): @optionalselfmethod def optionalself_mthd(self, *args): print('Optional-Self Method:', self, *args) |
用法如下:好的。
1 2 3 4 5 6 7 8 9 10 | In [3]: Test.optionalself_mthd(1, 2) Optional-Self Method: None 1 2 In [4]: x = Test() In [5]: x.optionalself_mthd(1, 2) Optional-Self Method: <test.Test object at 0x7fe80049d748> 1 2 In [6]: Test.instance_mthd(1, 2) Instance method: 1 2 |
当对实例调用时,
那么这是如何工作的呢?如何创建这样的新方法类型?好的。描述符协议
当python查找一个实例的字段时,即在执行
描述符协议是这三种内置方法的工作方式,它是
基本上,如果类dict有一个正确名称为1的值,python检查它是否有一个
例如,一个通常返回3的普通描述符:好的。
1 2 3 4 5 6 7 | class GetExample: def __get__(self, instance, cls): print("__get__", instance, cls) return 3 class Test: get_test = GetExample() |
用法如下:好的。
1 2 3 4 5 | In[22]: x = Test() In[23]: x.get_test __get__ <__main__.Test object at 0x7fe8003fc470> <class '__main__.Test'> Out[23]: 3 |
请注意,使用实例和类类型调用描述符。它也可用于课堂:好的。
1 2 3 | In [29]: Test.get_test __get__ None <class '__main__.Test'> Out[29]: 3 |
当描述符用于类而不是实例时,
这允许方法的简单实现:函数只实现描述符协议。当对函数调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | In [30]: x = object() In [31]: def test(self, *args): ...: print(f'Not really a method: self<{self}>, args: {args}') ...: In [32]: test Out[32]: <function __main__.test> In [33]: test.__get__(None, object) Out[33]: <function __main__.test> In [34]: test.__get__(x, object) Out[34]: <bound method test of <object object at 0x7fe7ff92d890>> |
我们可以做一些类似的事情来创建一个可以选择绑定到实例的新方法。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import functools class optionalselfmethod: def __init__(self, function): self.function = function functools.update_wrapper(self, function) def __get__(self, instance, cls): return boundoptionalselfmethod(self.function, instance) class boundoptionalselfmethod: def __init__(self, function, instance): self.function = function self.instance = instance functools.update_wrapper(self, function) def __call__(self, *args, **kwargs): return self.function(self.instance, *args, **kwargs) def __repr__(self): return f'<bound optionalselfmethod {self.__name__} of {self.instance}>' |
当你用
Was making a method this way (without args while not explicitly
decorated as staticmethods) intentional in Python 3's design? UPDATEDOk.
我认为这是有意的;但是目的是要消除未绑定的方法。在python 2和python 3中,
在python 2中,
在python 3中,
我不知道确切的理由,但我想,对类级函数的第一个参数进行类型检查被认为是不必要的,甚至是有害的;毕竟Python允许duck输入。好的。
Among the classic method types, what type of method is unknown_mthd?
Ok.
Why can unknown_mthd be called by the class without passing an
argument?Ok.
因为它只是一个普通的
您可以注意到,无论是通过实例还是通过类调用,
>1。如果一个特定的名称在实例dict中同时具有一个值,并且在类dict中同时具有一个描述符,则使用哪一个取决于它是哪种描述符。如果描述符只定义
@布伦巴恩很好地回答了你的问题。然而,这个答案增加了大量的细节:
首先,绑定和未绑定方法中的这种更改是特定于版本的,它与新样式或经典类不相关:
默认为2.x经典类
1 2 3 4 5 6 7 8 9 10 11 | >>> class A: ... def meth(self): pass ... >>> A.meth <unbound method A.meth> >>> class A(object): ... def meth(self): pass ... >>> A.meth <unbound method A.meth> |
默认情况下为3.x个新样式类
1 2 3 4 5 | >>> class A: ... def meth(self): pass ... >>> A.meth <function A.meth at 0x7efd07ea0a60> |
你已经在你的问题中提到过这一点了,把它两次作为提醒并不伤人。
1 2 3 | >>> # Python 2 >>> Test.unknown_mthd() TypeError: unbound method unknown_mthd() must be called with Test instance as first argument (got nothing instead) |
Moreover,
unknown_mthd does not accept args, and it can be bound to a class like astaticmethod ,Test.unknown_mthd() . However, it is not an explicitstaticmethod (no decorator)
1 - Was making a method this way (without args while not explicitly decorated as staticmethods) intentional in Python 3's design? UPDATED
我不能代表cpython开发人员发言,也不能自称是他们的代表,但根据我作为Python程序员的经验,他们似乎想摆脱一个糟糕的限制,特别是考虑到Python是非常动态的,而不是一种限制语言;为什么要测试传递给类方法的对象类型,然后ce是否将方法限制到类的特定实例?类型测试消除了多态性。如果只返回一个简单的函数,当一个方法通过一个功能上类似于静态方法的类被获取时,你可以把
2- Among the classic method types, what type of method is
unknown_mthd ?
3岁以下:
1 2 3 4 5 6 | >>> from types import * >>> class Test: ... def unknown_mthd(): pass ... >>> type(Test.unknown_mthd) is FunctionType True |
正如你所看到的,它只是3.x中的一个函数。在2.x下继续上一个会话:
1 2 3 4 | >>> type(Test.__dict__['unknown_mthd']) is FunctionType True >>> type(Test.unknown_mthd) is MethodType True |
3- Why can
unknown_mthd be called by the class without passing an argument?
同样,因为