Python 3:什么时候对实例属性的调用被解析为实例方法?

Python 3:什么时候对实例属性的调用被解析为实例方法?,python,python-3.x,instance-methods,Python,Python 3.x,Instance Methods,调用c.func(1)神奇地将实例c添加为第一个参数的条件是什么?我认为只有在类中将func定义为实例方法时才会发生这种情况。但是当func只是一个实例属性时,它似乎有时会起作用。对实例的绑定发生在您访问c.func时,而不是调用它时。执行c.func=c.func操作时,右侧的属性访问由类c处理,并计算为绑定方法。将其重新分配给c.func会将其分配给实例,但它已经是绑定方法,因此这不会改变行为 在newfunc示例中,您只定义了一个函数并分配了它,因此它从未被制作成实例方法。由于您直接将其分

调用
c.func(1)
神奇地将实例
c
添加为第一个参数的条件是什么?我认为只有在类中将
func
定义为实例方法时才会发生这种情况。但是当
func
只是一个实例属性时,它似乎有时会起作用。

对实例的绑定发生在您访问
c.func
时,而不是调用它时。执行
c.func=c.func
操作时,右侧的属性访问由类
c
处理,并计算为绑定方法。将其重新分配给
c.func
会将其分配给实例,但它已经是绑定方法,因此这不会改变行为

在newfunc示例中,您只定义了一个函数并分配了它,因此它从未被制作成实例方法。由于您直接将其分配给实例,因此该类没有机会拦截访问并将其包装到instancemethod中

您可以将绑定方法分配到任何您喜欢的地方,并且它仍然绑定到您获得它的实例。参见以下示例:

class C:
  def func(self, a):
    print(a)

c = C()
print(c.__dict__) # {}
c.func = c.func
# c.func is now an instance attribute, which hides instance method
print(c.__dict__) # {'func' : (bound method C.f of (__main.C object at ...))}
# how come it still works as if it's an instance method:
c.func(1) # prints 1

# with a regular function assigned to c.func, this won't happen:
def newfunc(self, a):
  print(a)
c.func = newfunc
c.func(1) # TypeError
注意,一旦我分配了
one.func2=two.func
,调用
one.func2
就会调用绑定到
two
的实例方法,而不是
one
。这是因为当我访问
two.func
时,我得到了一个绑定到该实例的方法。以后我把它分配到哪里并不重要。它仍然绑定到访问它的实例

如果直接访问未绑定的方法并尝试将其分配到其他位置,则会出现TypeError,因为该方法从未绑定:

>>> class C(object):
...     def __init__(self, name):
...         self.name = name
...     def func(self, a):
...         print("Called {0} with {1}".format(self.name, a))
>>> one = C("one")
>>> two = C("two")
>>> one.func(1)
Called one with 1
>>> two.func(1)
Called two with 1
>>> one.func2 = two.func
>>> one.func2(1)
Called two with 1
为了简洁地回答您的问题,
c.func
绑定到
c
,前提是
c
是具有方法
func
的类的实例,并且
c
本身没有实例属性
func
。(如果
c
确实有一个同名的实例属性,则该属性将重写类1,因此该类不需要进行包装以将方法绑定到实例。)

c.func(1)当方法
c.func
绑定到对象
c
时,
将神奇地添加实例
c
作为第一个参数


作为该类属性的函数将自动绑定到该类的实例。因此,紧接着
c=c()
c.func
不同于
c.func
,它只接受一个更少的参数,而
c
神奇地成为第一个参数。这就是为什么
c.func=c.func
不会改变任何东西,而且一切都正常工作的原因

如果要使用新实例方法修补类的实例,则需要将函数绑定到实例。以下是一些选项:

  • 使用
    类型.MethodType

    >>> def newFunc(self, a):
    ...     print("Newfunc of {0} called with {1}".format(self.name, a))
    >>> C.newFunc = newFunc
    >>> one.newFunc(1)
    Newfunc of one called with 1
    
  • 使用
    functools.partial

    import types
    c.func = types.MethodType(newfunc, c)
    

请注意,第二种方法模拟了该行为,但略有不同,因为
type(c.func)
将是
而不是
,因此第一种方法可能更可取。

因为
c.func
不再是一个普通函数,而是一个绑定到instanceShouldn
def newfunc(self,a)的方法:
be
def newfunc(a):
?@Larry:No.方法将实例作为第一个参数。@LarryBattle:这就是我的观点。对
c.func
的第一次调用神奇地插入了
c
作为第一个参数,而第二次调用没有。请注意,在这两种情况下,
c.func
都“只是”一个实例属性。
c.func
在这两种情况下都是一个实例属性,但在一种情况下,该属性的值是一个绑定方法,而在另一种情况下它是一个函数。“作为类属性的函数会自动绑定到该类的新实例。”-如果删除“新”一词,可能会更清楚。否则,预先存在的实例似乎不会绑定到新定义的类函数。+1-thx,我没有意识到
MethodType
partial
之间的关系。Python如何确定“类是否具有方法
func
”?它只是检查它是一个类属性,并且是可调用的吗?看起来不是这样,因为如果我分配
C.newFunc=len
,它就不会绑定到
C
的实例,即使
len
是可调用的。如果我正确理解文档,它只对用户定义的函数执行此操作?@max:它必须是“函数”类型的对象
len
不起作用,因为它的类型是“内置函数”。不幸的是,内置函数与“真正的”Python函数不同,这就是它出现的一个例子。但是,如果您尝试用Python编写的库函数(例如,
C.func=collections.namedtuple
),您可以看到它是有效的。
import types
c.func = types.MethodType(newfunc, c)
import functools
c.func = functools.partial(newfunc, c)