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
不再是一个普通函数,而是一个绑定到instanceShouldndef newfunc(self,a)的方法:
bedef 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)