函数、非绑定方法和绑定方法之间有什么区别?

2025-01-17 09:23:00
admin
原创
94
摘要:问题描述:我之所以问这个问题,是因为这个答案的评论区有讨论。我已经理解了 90% 了。In [1]: class A(object): # class named 'A' ...: def f1(self): pass ...: In [2]: a = A() # an instanc...

问题描述:

我之所以问这个问题,是因为这个答案的评论区有讨论。我已经理解了 90% 了。

In [1]: class A(object):  # class named 'A'
   ...:     def f1(self): pass
   ...:
In [2]: a = A()  # an instance

f1有三种不同的形式:

In [3]: a.f1  # a bound method
Out[3]: <bound method a.f1 of <__main__.A object at 0x039BE870>>
In [4]: A.f1  # an unbound method
Out[4]: <unbound method A.f1>
In [5]: a.__dict__['f1']  # doesn't exist
KeyError: 'f1'
In [6]: A.__dict__['f1']  # a function
Out[6]: <function __main__.f1>

f1 描述的绑定方法非绑定方法函数对象有什么区别?如何调用这三个对象?它们如何相互转化?这方面的文档很难理解。


解决方案 1:

函数由语句或创建。在 Python 2 下,当函数出现在语句主体中(或传递给类构造调用)时,它将转换为未绑定方法。(Python 3 没有未绑定方法;见下文。)当在类实例上访问函数时,它将转换为绑定方法,该方法会自动将实例作为第一个参数提供给方法。def`lambdaclasstype`self

def f1(self):
    pass

f1是一个函数

class C(object):
    f1 = f1

现在C.f1是一种不受约束的方法。

>>> C.f1
<unbound method C.f1>
>>> C.f1.im_func is f1
True

我们还可以使用type类构造函数:

>>> C2 = type('C2', (object,), {'f1': f1})
>>> C2.f1
<unbound method C2.f1>

我们可以f1手动转换为非绑定方法:

>>> import types
>>> types.MethodType(f1, None, C)
<unbound method C.f1>

未绑定的方法受类实例上的访问约束:

>>> C().f1
<bound method C.f1 of <__main__.C object at 0x2abeecf87250>>

访问转化为通过描述符协议调用:

>>> C.f1.__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>

综合起来:

>>> types.MethodType(f1, None, C).__get__(C(), C)
<bound method C.f1 of <__main__.C object at 0x2abeecf87310>>

或者直接:

>>> types.MethodType(f1, C(), C)                
<bound method C.f1 of <__main__.C object at 0x2abeecf871d0>>

函数和未绑定方法之间的主要区别在于后者知道它绑定到哪个类;调用或绑定未绑定方法需要其类类型的实例:

>>> f1(None)
>>> C.f1(None)
TypeError: unbound method f1() must be called with C instance as first argument (got NoneType instance instead)
>>> class D(object): pass
>>> f1.__get__(D(), D)
<bound method D.f1 of <__main__.D object at 0x7f6c98cfe290>>
>>> C.f1.__get__(D(), D)
<unbound method C.f1>

由于函数和未绑定方法之间的区别非常小,Python 3消除了这种区别;在 Python 3 下,访问类实例上的函数只会给你函数本身:

>>> C.f1
<function f1 at 0x7fdd06c4cd40>
>>> C.f1 is f1
True

那么,在 Python 2 和 Python 3 中,这三个都是等效的:

f1(C())
C.f1(C())
C().f1()

将函数绑定到实例会将其第一个参数(通常称为self)固定到实例。因此绑定方法C().f1等效于以下任一操作:

(lamdba *args, **kwargs: f1(C(), *args, **kwargs))
functools.partial(f1, C())

解决方案 2:

很难理解

嗯,这是一个相当困难的话题,它与描述符有关。

让我们从函数开始。这里一切都很清楚 - 您只需调用它,执行时会传递所有提供的参数:

>>> f = A.__dict__['f1']
>>> f(1)
1

TypeError如果参数数量出现任何问题,则会引发常规问题:

>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f1() takes exactly 1 argument (0 given)

现在,方法。方法是带有一点趣味的函数。描述符在这里发挥作用。如数据模型中所述,A.f1和分别A().f1被翻译成A.__dict__['f1'].__get__(None, A)和。这些的结果与原始函数不同。这些对象是原始对象的包装器,包含一些额外的逻辑。type(a).__dict__['f1'].__get__(a, type(a))`__get__f1f1`

在这种情况下,unbound method逻辑包括检查第一个参数是否是以下实例A

>>> f = A.f1
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method f1() must be called with A instance as first argument (got nothing instead)
>>> f(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method f1() must be called with A instance as first argument (got int instance instead) 

如果此检查成功,它将f1以该实例作为第一个参数执行 original :

>>> f(A())
<__main__.A object at 0x800f238d0>

请注意,该im_self属性是None

>>> f.im_self is None
True

如果出现bound method这种逻辑,则会立即向 original 提供其创建f1的实例(该实例实际上存储在属性中):A`im_self`

>>> f = A().f1
>>> f.im_self
<__main__.A object at 0x800f23950>
>>> f()
<__main__.A object at 0x800f23950>

所以,bound意味着底层函数与某个实例绑定。unbound意味着它仍然是绑定的,但仅限于一个类。

解决方案 3:

函数对象是通过函数定义创建的可调用对象。绑定方法和非绑定方法都是通过点二元运算符调用的描述符创建的可调用对象。

绑定和非绑定方法对象有 3 个主要属性:im_func是类中定义的函数对象、im_class是类、im_self是类实例。对于非绑定方法,im_selfNone

当调用绑定方法时,它会以im_funcim_self作为第一个参数,后面跟着它的调用参数。未绑定方法仅使用其调用参数来调用底层函数。

从 Python 3 开始,没有未绑定的方法。Class.method返回对方法的直接引用。

解决方案 4:

请参阅Python 2和Python 3文档以了解更多详细信息。

我的解释如下。

课程Function片段:

Python 3:

class Function(object):
    . . .
    def __get__(self, obj, objtype=None):
        "Simulate func_descr_get() in Objects/funcobject.c"
        if obj is None:
            return self
        return types.MethodType(self, obj)

Python 2:

class Function(object):
    . . .
    def __get__(self, obj, objtype=None):
        "Simulate func_descr_get() in Objects/funcobject.c"
        return types.MethodType(self, obj, objtype)
  1. 如果调用函数时没有使用类或实例,则它是一个普通函数。

  2. 如果从类或实例调用函数,__get__则会调用它来检索包装函数:

a.B.x与 相同B.__dict__['x'].__get__(None, B)。在 Python 3 中,这将返回普通函数。在 Python 2 中,这将返回未绑定的函数。

b.b.x与 相同type(b).__dict__['x'].__get__(b, type(b)。这将在 Python 2 和 Python 3 中返回一个绑定方法,这意味着self将隐式传递为第一个参数。

解决方案 5:

函数、非绑定方法和绑定方法之间有什么区别?

从突破性的角度看,函数并没有什么区别。Python 面向对象特性是建立在基于函数的环境之上的。

受约束等于:

该函数是否将cls)或对象实例self)作为第一个参数?

以下是示例:

class C:

    #instance method 
    def m1(self, x):
        print(f"Excellent m1 self {self} {x}")

    @classmethod
    def m2(cls, x):
        print(f"Excellent m2 cls {cls} {x}")

    @staticmethod
    def m3(x):
        print(f"Excellent m3 static {x}")    

ci=C()
ci.m1(1)
ci.m2(2)
ci.m3(3)

print(ci.m1)
print(ci.m2)
print(ci.m3)
print(C.m1)
print(C.m2)
print(C.m3)

输出:

Excellent m1 self <__main__.C object at 0x000001AF40319160> 1
Excellent m2 cls <class '__main__.C'> 2
Excellent m3 static 3
<bound method C.m1 of <__main__.C object at 0x000001AF40319160>>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>
<function C.m1 at 0x000001AF402FBB70>
<bound method C.m2 of <class '__main__.C'>>
<function C.m3 at 0x000001AF4023CBF8>

输出显示静态函数m3永远不会被调用绑定
C.m2被绑定到类是C因为我们发送的cls参数是类指针。

ci.m1并且ci.m2都是绑定的;ci.m1因为我们发送的self是指向实例的指针,并且ci.m2因为实例知道该类是绑定的;)。

综上所述,您可以根据方法采用的第一个参数将方法绑定到类或类对象。如果方法未绑定,则可以将其称为未绑定。


请注意,该方法可能不是该类的原始部分。查看Alex Martelli 的此答案以了解更多详细信息。

解决方案 6:

今天我看到一件有趣的事情是,当我将一个函数分配给类成员时,它就变成了一个未绑定的方法。例如:

class Test(object):
    @classmethod
    def initialize_class(cls):
        def print_string(self, str):
            print(str)
        # Here if I do print(print_string), I see a function
        cls.print_proc = print_string
        # Here if I do print(cls.print_proc), I see an unbound method; so if I
        # get a Test object o, I can call o.print_proc("Hello")
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用