方法和函数之间的区别,在Python中与C++相比

Q-b*_*uit 49 python oop methods function

我正在使用Code Academy的Python教程,我对方法和函数的定义有点困惑.从教程:

你已经知道了一些我们所使用(或创建)字符串的内置功能,例如.upper(),.lower(),str(),和len().

从C++来了,我想.upper().lower()将调用的方法和len()str()功能.在本教程中,这些术语似乎可以互换使用.

Python是否以C++的方式区分方法和函数?

方法和函数之间的差异不同,我问的是Python的细节.术语"方法"和"功能"似乎并不总是遵循链接问题的已接受答案中给出的定义.

Kru*_*lur 49

功能是在Python一个可调用对象,即,可以使用被称为呼叫操作员(尽管其它的目的可以通过实施模拟函数__call__).例如:

>>> def a(): pass
>>> a
<function a at 0x107063aa0>
>>> type(a)
<type 'function'>
Run Code Online (Sandbox Code Playgroud)

方法是一类特殊的功能,一个可被结合绑定.

>>> class A:
...   def a(self): pass
>>> A.a
<unbound method A.a>
>>> type(A.a)
<type 'instancemethod'>

>>> A().a
<bound method A.a of <__main__.A instance at 0x107070d88>>
>>> type(A().a)
<type 'instancemethod'>
Run Code Online (Sandbox Code Playgroud)

当然,无法调用未绑定的方法(至少不能直接将实例作为参数传递):

>>> A.a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method a() must be called with A instance as first argument (got nothing instead)
Run Code Online (Sandbox Code Playgroud)

在Python中,在大多数情况下,您将不会注意到绑定方法,函数或可调用对象(即实现的对象__call__)或类构造函数之间的区别.它们看起来都一样,只是有不同的命名约定.在引擎盖下,物体看起来可能大不相同.

这意味着绑定方法可以用作函数,这是使Python如此强大的许多小事之一

>>> b = A().a
>>> b()
Run Code Online (Sandbox Code Playgroud)

这也意味着即使len(...)和之间存在根本区别str(...)(后者是一个类型构造函数),在深入挖掘之前,你不会注意到差异:

>>> len
<built-in function len>
>>> str
<type 'str'>
Run Code Online (Sandbox Code Playgroud)

  • 方法与类相关联,*bound*方法与类的实例相关联. (8认同)
  • 我同意该方法是函数的子集.我不会说函数是任何可调用对象.一个类是可调用的,但我不会称之为函数. (4认同)
  • 方法是与特定类相关的函数,对吧? (2认同)

eyq*_*uem 5

\n

如果您仍然不了解方法的工作原理,那么看一下实现也许可以澄清问题。当引用的实例属性不是数据属性时,将搜索其类。如果名称表示有效的类属性,即函数对象,则通过将实例对象和刚刚在抽象对象中找到的函数对象打包(指向)来创建方法对象:\n 这是方法对象。当使用参数列表调用方法对象时,将从实例对象和参数列表构造一个新的参数列表,并使用此新参数列表调用函数对象。

\n\n

http://docs.python.org/2/tutorial/classes.html#method-objects

\n
\n\n

仔细阅读这段摘录。

\n\n

它的意思是 :

\n\n

1) 实例并不真正拥有一个对象,该对象是一个方法,而该方法将成为其属性。\n事实上,实例
中根本不存在“方法”属性(是对象的命名空间)__dict____dict__

\n\n

2)事实上,当调用“method”属性时,实例似乎有一个“方法”,这是由于一个过程,而不是实例的名称空间内存在方法对象

\n\n

3)此外,类的命名空间中并不真正存在方法对象。

\n\n

但是实例是有区别的,因为当这样的调用完成时,一定有某个地方会导致真正的方法对象,不是吗?

\n\n

为了表述方便,所谓的类的“方法”属性实际上是类的命名空间中的属性的函数对象。
\n也就是说,pair (函数的标识符,函数)是一个类的成员__dict__,这个属性允许解释器在执行方法调用时构造一个方法对象。

\n\n

4)同样,当调用“method”属性时,类似乎有一个“方法”,这是由于一个过程,而不是由于类的命名空间内存在方法对象

\n\n
\n

编辑我对此不再确定;见最后

\n
\n\n

5)方法对象(不是“方法”对象;我的意思是真正的对象实际上是一个方法,摘录中描述的)是在调用时创建的,它之前不存在。
\n它是一种包装器:它打包指向实例对象和该方法所基于的函数对象的指针。

\n\n

因此,方法是基于函数的。对我来说,这个函数是持有所述“方法”的类的真正属性,因为这个函数确实属于该类的名称空间( ):这个函数在打印时__dict__被描述为 a 。\n可以使用别名或从方法对象访问此函数(请参阅下面的代码)<function ......>__dict__
im_func__func__

\n\n

\n\n

我相信这些概念并不是很为人所知和理解。但下面的代码证明了我所说的。

\n\n
class A(object):\n    def __init__(self,b=0):\n        self.b = b\n    print \'The __init__ object :\\n\',__init__\n\n    def addu(self):\n        self.b = self.b + 10\n    print \'\\nThe addu object :\\n\',addu\n\n\nprint \'\\nThe A.__dict__  items :\\n\',\nprint \'\\n\'.join(\'  {0:{align}11}  :  {1}\'.format(*it,align=\'^\')\n                for it in A.__dict__.items())\na1 = A(101)\na2 = A(2002)\n\nprint \'\\nThe a1.__dict__  items:\'\nprint \'\\n\'.join(\'  {0:{align}11}  :  {1}\'.format(*it,align=\'^\')\n                for it in a1.__dict__.items())\n\nprint \'\\nThe a2.__dict__  items:\'\nprint \'\\n\'.join(\'  {0:{align}11}  :  {1}\'.format(*it,align=\'^\')\n                for it in a2.__dict__.items())\n\nprint \'\\nA.addu.__func__ :\',A.addu.__func__\nprint id(A.addu.__func__),\'==\',hex(id(A.addu.__func__))\nprint\n\nprint \'A.addu :\\n  \',\nprint A.addu,\'\\n  \',id(A.addu),\'==\',hex(id(A.addu))\n\nprint \'a1.addu :\\n  \',\nprint a1.addu,\'\\n  \',id(a1.addu),\'==\',hex(id(a1.addu))\nprint \'a2.addu :\\n  \',\nprint a2.addu,\'\\n  \',id(a2.addu),\'==\',hex(id(a2.addu))\n\na2.addu()\nprint \'\\na2.b ==\',a2.b\n\nprint \'\\nThe A.__dict__  items :\\n\',\nprint \'\\n\'.join(\'  {0:{align}11}  :  {1}\'.format(*it,align=\'^\')\n                for it in A.__dict__.items())\n
Run Code Online (Sandbox Code Playgroud)\n\n

结果

\n\n
The __init__ object :\n<function __init__ at 0x011E54B0>\n\nThe addu object :\n<function addu at 0x011E54F0>\n\nThe A.__dict__  items :\n  __module__   :  __main__\n     addu      :  <function addu at 0x011E54F0>\n   __dict__    :  <attribute \'__dict__\' of \'A\' objects>\n  __weakref__  :  <attribute \'__weakref__\' of \'A\' objects>\n    __doc__    :  None\n   __init__    :  <function __init__ at 0x011E54B0>\n\nThe a1.__dict__  items:\n       b       :  101\n\nThe a2.__dict__  items:\n       b       :  2002\n\nA.addu.__func__ : <function addu at 0x011E54F0>\n18765040 == 0x11e54f0\n\nA.addu :\n   <unbound method A.addu> \n   18668040 == 0x11cda08\na1.addu :\n   <bound method A.addu of <__main__.A object at 0x00CAA850>> \n   18668040 == 0x11cda08\na2.addu :\n   <bound method A.addu of <__main__.A object at 0x011E2B90>> \n   18668040 == 0x11cda08\n\na2.b == 2012\n\nThe A.__dict__  items :\n  __module__   :  __main__\n     addu      :  <function addu at 0x011E54F0>\n   __dict__    :  <attribute \'__dict__\' of \'A\' objects>\n  __weakref__  :  <attribute \'__weakref__\' of \'A\' objects>\n    __doc__    :  None\n   __init__    :  <function __init__ at 0x011E54B0>\n
Run Code Online (Sandbox Code Playgroud)\n\n

\n\n

编辑

\n\n

有些事情困扰着我,我不知道这个主题的深层内容:

\n\n

上面的代码表明A.addua1.addua2.addu都是同一个方法对象,具有唯一的标识。
然而,之所以A.addu称为未绑定方法,是因为它没有任何有关特定实例的信息,
而之所以a1.addu称为a2.addu绑定方法,是因为每个方法都具有指定该方法的操作必须涉及的实例的信息。
\n从逻辑上讲,对我来说,这意味着这 3 种情况的方法应该不同。

\n\n

但是这三个标识都是相同的,而且该标识与该方法所基于的函数的标识不同。
\n它得出的结论是,该方法实际上是一个存在于内存中的对象,并且它不会从一个实例的一次调用到另一个实例的另一个调用发生变化。

\n\n

然而,打印类的名称空间__dict__,即使在创建实例和调用“方法”之后addu(),该名称空间也不会公开可识别为与函数不同的方法对象的新对象addu

\n\n

这是什么意思 ?
\n它给我的印象是,一旦创建了方法对象,它就不会被销毁,而是存在于内存(RAM)中。
\n但它是隐藏的,只有构成解释器功能的进程才知道如何以及在哪里找到它。
\n这个隐藏对象,即真正的方法对象,必须能够更改对必须应用该函数的实例的引用,或者None如果将其作为未绑定方法调用,则必须能够更改引用。在我看来,这只是头脑风暴的假设。

\n\n

有人知道这次审讯的情况吗?

\n\n
\n\n

.upper为了回答这个问题,调用和.lower 函数可以被认为是正确的,因为实际上它们基于函数作为类的每个方法。

\n\n

但是,以下结果很特殊,可能是因为它们是内置方法/函数,而不是我的代码中的用户方法/函数。

\n\n
x = \'hello\'\nprint x.upper.__func__\n
Run Code Online (Sandbox Code Playgroud)\n\n

结果

\n\n
    print x.upper.__func__\nAttributeError: \'builtin_function_or_method\' object has no attribute \'__func__\'\n
Run Code Online (Sandbox Code Playgroud)\n

  • 我刚刚意识到 ``A.adu`` 给出 ``&lt;unbound method A.adu&gt;`` 和 ``type(A.adu`` 给出 ``&lt;type 'instancemethod'&gt;`` ---WHILE --- ``A.__dict__['addu']`` 给出 ``&lt;function addu at 0x011E54F0&gt;`` 和 ``type(A.__dict__['adu'])`` 给出 ``&lt;type 'function' &gt;`` 。根据到达对象的方式确实存在差异。使用 ``.`` ,点符号,触发隐藏机制的动作,这就是我的答案和这两个结果所反映的。方法确实是神秘的东西,在Python内部机制的秘密黑暗中运作。我的观点 (2认同)
  • 首先感谢您的精彩回答。关于您的疑问:每次访问“a1.adu”时,都会创建一个新的(临时)对象,然后直接销毁。巧合的是,这些对象具有相同的 id(因为它们是在相同的内存地址创建的)。但是,如果保留对创建的对象的引用,您将看到不同的 id:`id(a1.adu) == id(a1.adu)` 为 True,因为临时对象被相继创建和销毁,并且相同的内存被创建和销毁。重用,但 `a1.adu is a1.adu` 为 False,因为临时对象同时处于活动状态并且具有不同的地址。 (2认同)