我正在使用Code Academy的Python教程,我对方法和函数的定义有点困惑。从教程:
您已经了解我们在(或创建)字符串上使用的一些内置函数,例如
.upper()
,.lower()
,str()
和{{1 }}。
来自C ++,我认为len()
和.upper()
将被称为方法,.lower()
和len()
函数。在本教程中,这些术语似乎可以互换使用。
Python是否以C ++的方式区分方法和函数?
与Difference between a method and a function不同,我问的是Python的细节。术语“方法”和“功能”似乎并不总是遵循链接问题的已接受答案中给出的定义。
答案 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.addu
和a1.addu
都是相同的方法对象,具有唯一标识。
但是,a2.addu
被称为未绑定方法,因为它没有关于特定实例的任何信息,
A.addu
和a1.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'
让我们创建上述类的实例。我们会通过将class object, i.e. MyClass()
分配给var x
x = MyClass()
下面,
不要忘记,当我们将x分配给MyClass()
时,function object MyClass.f
用于定义(内部)method object x.f
答案 4 :(得分:1)
从this answer提取代码段
以下是更改
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'>