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
调用c.func(1)
神奇地将实例c
添加为第一个参数的条件是什么?我认为只有在func
被定义为类中的实例方法时才会发生这种情况。但是,当func
只是一个实例属性时,似乎有时有效。
答案 0 :(得分:3)
当您访问 c.func
时,会发生与实例的绑定,而不是在您调用它时。执行c.func = c.func
时,右侧的属性访问由类C
处理,并对绑定方法进行评估。将其重新分配给c.func
会将其分配给实例,但它已经是绑定方法,因此这不会改变行为。
在你的newfunc示例中,您刚刚定义了一个函数并对其进行了分配,因此它永远不会成为实例方法。由于您直接将其分配给实例,因此该类没有机会拦截访问并将其包装在实例方法中。
您可以在任何您喜欢的地方分配绑定方法,它仍然绑定到您获得它的实例。请参阅以下示例:
>>> 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
请注意,在我分配one.func2 = two.func
后,调用one.func2
会调用绑定到two
的实例方法,而不是one
。这是因为当我访问two.func
时,我得到了一个绑定到该实例的方法。我稍后分配它并不重要。它仍然与访问它的实例绑定。
如果您直接访问未绑定的方法并尝试将其分配到其他位置,那么您将获得TypeError,因为该方法从未绑定:
>>> one.func3 = C.func
>>> one.func3(1)
Traceback (most recent call last):
File "<pyshell#16>", line 1, in <module>
one.func3(1)
TypeError: unbound method func() must be called with C instance as first argument (got int instance instead)
另请注意,如果将方法添加到类,则在实例上调用时它将起作用,即使在将方法添加到类之前创建了实例也是如此。保持与以前相同的one
对象,我可以这样做:
>>> 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
要简洁地回答您的问题,如果c.func
是具有方法c
和c
的类的实例,func
将绑定到c
本身不具有实例属性func
。 (如果c
确实具有相同名称的实例属性,那么它将覆盖第一类,因此该类不会进行包装以将方法绑定到实例。)
答案 1 :(得分:1)
c.func(1)
绑定到对象c
时, c.func
会将实例c
神奇地添加为第一个参数。
作为类属性的函数会自动绑定到该类的实例。因此,在c = C()
之后,c.func
与C.func
不同,因为它接受少一个参数,c
神奇地成为第一个参数。这就是c.func = c.func
没有改变任何东西,一切正常的原因。
如果要使用新的实例方法修补类的实例,则需要将该函数绑定到实例。以下是一些选项:
使用types.MethodType
:
import types
c.func = types.MethodType(newfunc, c)
使用functools.partial
:
import functools
c.func = functools.partial(newfunc, c)
请注意,第二种方法模仿了行为,但略有不同,因为type(c.func)
将是<type 'functools.partial'>
而不是<type 'instancemethod'>
,所以第一种方法可能更可取。