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

时间:2014-01-07 21:00:40

标签: python function oop methods

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

  

您已经了解我们在(或创建)字符串上使用的一些内置函数,例如.upper().lower()str()和{{1 }}。

来自C ++,我认为len().upper()将被称为方法,.lower()len()函数。在本教程中,这些术语似乎可以互换使用。

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

Difference between a method and a function不同,我问的是Python的细节。术语“方法”和“功能”似乎并不总是遵循链接问题的已接受答案中给出的定义。

5 个答案:

答案 0 :(得分:49)

函数是Python中的可调用对象,即可以使用调用操作符调用(尽管其他对象可以通过实现__call__来模拟函数) 。例如:

>>> def a(): pass
>>> a
<function a at 0x107063aa0>
>>> type(a)
<type 'function'>

方法是一个特殊的功能类,可以绑定未绑定

>>> 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'>

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

>>> 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)

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

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

>>> b = A().a
>>> b()

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

>>> len
<built-in function len>
>>> str
<type 'str'>

答案 1 :(得分:4)

  

如果你仍然不明白方法是如何工作的,那么看看   实施也许可以澄清问题。当一个实例属性   引用的不是数据属性,搜索其类。如果   name表示一个有效的class属性,它是一个函数对象,a   方法对象是通过打包(指向)实例对象来创建的   和一个在抽象对象中一起找到的函数对象:   这是方法对象。用方法调用方法对象时   参数列表,从实例构造一个新的参数列表   对象和参数列表,以及调用函数对象   这个新的参数列表。

     

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

仔细阅读这段摘录。

这意味着:

1)实例并不真正将对象作为其属性的方法 事实上,根本没有&#34;方法&#34;实例的__dict__中的属性(__dict__是对象的命名空间)

2)一个实例似乎有一个&#34;方法&#34;当一个&#34;方法&#34;调用属性,是由于进程,而不是实例的命名空间中存在方法对象

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

但是与实例存在差异,因为在完成这样的调用时必定会有某些东西导致真正的方法对象,不能吗?

什么叫做&#34;方法&#34;为了方便起见,类的属性实际上是一个 function 对象,它是类的命名空间中的属性。
也就是说,一对(函数,函数的标识符)是一个类的__dict__的成员,并且该属性允许该解释器构造一个方法对象当执行方法调用时。

4)同样,一个班级似乎有一个&#34;方法&#34;当一个&#34;方法&#34;调用属性,是由于进程,而不是在类的名称空间内存在方法对象

  

编辑我不再确定;最后看到

5)方法对象(不是&#34;方法&#34;对象;我的意思是真正的对象实际上是一种方法`,摘录中描述的内容)目前是创建通话中,它之前并不存在。
它是一种包装器:它包含指向实例对象的指针和方法所基于的函数对象。

因此,一种方法基于一种功能。这个函数对我来说是持有所述&#34;方法&#34;的类的真实属性,因为这个函数实际上属于类的命名空间(__dict__):这个函数被描述为{{ 1}}打印<function ......>时 可以使用别名__dict__im_func从方法对象访问此函数(请参阅下面的代码)

我认为这些观念并不常见,也不为人所知。但是下面的代码证明了我说的话。

__func__

结果

class A(object):
    def __init__(self,b=0):
        self.b = b
    print 'The __init__ object :\n',__init__

    def addu(self):
        self.b = self.b + 10
    print '\nThe addu object :\n',addu


print '\nThe A.__dict__  items :\n',
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in A.__dict__.items())
a1 = A(101)
a2 = A(2002)

print '\nThe a1.__dict__  items:'
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in a1.__dict__.items())

print '\nThe a2.__dict__  items:'
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in a2.__dict__.items())

print '\nA.addu.__func__ :',A.addu.__func__
print id(A.addu.__func__),'==',hex(id(A.addu.__func__))
print

print 'A.addu :\n  ',
print A.addu,'\n  ',id(A.addu),'==',hex(id(A.addu))

print 'a1.addu :\n  ',
print a1.addu,'\n  ',id(a1.addu),'==',hex(id(a1.addu))
print 'a2.addu :\n  ',
print a2.addu,'\n  ',id(a2.addu),'==',hex(id(a2.addu))

a2.addu()
print '\na2.b ==',a2.b

print '\nThe A.__dict__  items :\n',
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in A.__dict__.items())

修改

有些事让我感到不安,我也不知道这个主题的深层内脏:

上述代码显示The __init__ object : <function __init__ at 0x011E54B0> The addu object : <function addu at 0x011E54F0> The A.__dict__ items : __module__ : __main__ addu : <function addu at 0x011E54F0> __dict__ : <attribute '__dict__' of 'A' objects> __weakref__ : <attribute '__weakref__' of 'A' objects> __doc__ : None __init__ : <function __init__ at 0x011E54B0> The a1.__dict__ items: b : 101 The a2.__dict__ items: b : 2002 A.addu.__func__ : <function addu at 0x011E54F0> 18765040 == 0x11e54f0 A.addu : <unbound method A.addu> 18668040 == 0x11cda08 a1.addu : <bound method A.addu of <__main__.A object at 0x00CAA850>> 18668040 == 0x11cda08 a2.addu : <bound method A.addu of <__main__.A object at 0x011E2B90>> 18668040 == 0x11cda08 a2.b == 2012 The A.__dict__ items : __module__ : __main__ addu : <function addu at 0x011E54F0> __dict__ : <attribute '__dict__' of 'A' objects> __weakref__ : <attribute '__weakref__' of 'A' objects> __doc__ : None __init__ : <function __init__ at 0x011E54B0> A.addua1.addu都是相同的方法对象,具有唯一标识。
但是,a2.addu被称为未绑定方法,因为它没有关于特定实例的任何信息,
A.addua1.addu是绑定方法,因为每个方法都有指定必须关注方法操作的实例的信息。
从逻辑上讲,对我来说,这意味着对于这三种情况中的每种情况,该方法应该是不同的。

三者的身份相同,而且此身份与该方法所依据的功能的身份不同。
它得出的结论是,该方法实际上是一个存在于内存中的对象,并且它不会从一个实例调用从另一个实例更改为另一个实例。

HOWEVER ,打印类的名称空间a2.addu,即使在创建实例和调用&#34;方法&#34;之后也是如此。 __dict__,此命名空间不会公开可以识别与addu()函数不同的方法对象的新对象。

这是什么意思?
它给我的印象是,一旦创建了一个方法对象,它就不会被破坏,它会存在于内存(RAM)中。 但它隐藏起来,只有形成互操作者功能的过程知道如何以及在何处找到它。
此隐藏对象(实际方法对象)必须能够更改对必须应用该函数的实例的引用,或者如果将其作为未绑定方法调用则引用addu。这对我来说似乎是什么,但它只是头脑风暴的假设。

有人对此审讯有所了解吗?


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

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

.lower

结果

x = 'hello'
print x.upper.__func__

答案 2 :(得分:2)

基本上,是的,Python确实区分了它们,但在Python中,将方法视为函数的子集是很常见的。方法与类或实例相关联,并且&#34;独立函数&#34;不是。作为方法的东西也是一个函数,但是可能存在不是方法的函数。

正如Jon Clements在他的评论中提到的那样,区别并不像C ++那样铁定。独立功能可以被转换为#34;在运行时将方法分配给方法,并且方法可以以这样的方式分配给变量,使得它们的行为与独立函数没有区别。所以方法和功能之间的界限是可渗透的。

答案 3 :(得分:2)

在以下类定义中:

class MyClass:
    """A simple example class"""
    def f(self):
        return 'hello world'
  • MyClass
  • 功能 f()
  • 方法无(实际上,不适用)

让我们创建上述类的实例。我们会通过将class object, i.e. MyClass()分配给var x

来完成此操作
  x = MyClass()

下面,

  • 功能
  • 方法 x.f()

不要忘记,当我们将x分配给MyClass()

时,function object MyClass.f用于定义(内部)method object x.f

答案 4 :(得分:1)

this answer提取代码段

以下是更改

  • 未绑定的方法(绑定到类对象的方法)不再可用。
  • 未绑定方法->函数
  • 从Python 3中删除了
  • 类型instancemethod

有关this answer上方法的更多信息

关于Python docs上的方法

有关Python docs上的课程的更多信息

import sys
print(sys.version)
# 3.9.0rc2 (tags/v3.9.0rc2:2bd31b5, Sep 17 2020, 00:58:12) [MSC v.1927 64 bit (AMD64)]

class A:
  def a(self): pass

print(A.a)
# <unbound method A.a>
# <function A.a at 0x00000200FBE121F0>

print(type(A.a))
# <type 'instancemethod'>
# <class 'function'>

print(A().a)
# <bound method A.a of <__main__.A instance at 0x107070d88>>
# <bound method A.a of <__main__.A object at 0x00000200FBB12640>>

print(type(A().a))
# <type 'instancemethod'>
# <class 'method'>