python - injecting a python class member function
问题描述
The following code works in python3, but not python2. To be clear, I am trying to inject/monkey patch a member function from another class (Class1) into the target class (Class2) so that the function uses appropriate member attributes from self.
Why does it work in python3, and what else can I do to get the desired behavior in python2?
class Parent:
def foo(self):
print("foo of class Parent: instance " + self.__class__.__name__)
def __init__(self):
self.d = {"foo": self.__class__.foo}
def bar(self):
self.d['foo'](self)
self.d['foo'] = Child1.foo
self.d['foo'](self)
class Child1(Parent):
def foo(self):
print("foo of class Child1: instance " + self.__class__.__name__)
class Child2(Parent):
pass
Child2().bar()
解决方案
What you're looking to do here is to call the unbound method Child2.foo
with a self
that isn't a Child2
.
This is illegal, and Python 2 will detect that and raise a TypeError
that explains exactly what's wrong:
TypeError: unbound method foo() must be called with Child1 instance as first argument (got Child2 instance instead)
In Python 3, there are no unbound method objects; unbound methods are just plain old functions. So they can't check that you're trying to do anything illegal. And then, because you aren't actually using the fact that self
is a Child2
inside the foo
method, you get away with it.
But you can't inject methods that actually use the Child2
-ness of Child2
this way; they'll just end up raising a TypeError
or AttributeError
or calling the wrong methods. It will only work for methods that had no reason to be methods in the first place.
If you really want this behavior in Python 2 anyway, you can get it by extracting the function out of the unbound method:
self.d['foo'] = Child1.foo.__func__
(If you want to work with even older 2.x, use im_func
instead of __func__
there.)
Now, it's not a method at all—and if you tried to actually bind it to self
with the descriptor protocol or by constructing a MethodType
instead, you'd get the same old TypeError
. But it is a function, and you can call it with whatever argument you want as a function. And, because your function doesn't do anything with that self
parameter that requires it to be a Child2
, it will work.
While we're at it, you almost certainly want Parent
to be a new-style class here. And you probably want to use the same code in Python 2 and Python 3, not different code for the same behavior in both. So:
class Parent(object): # makes it a new-style class in 2.x as well as 3.x
def foo(self):
print("foo of class Parent: instance " + self.__class__.__name__)
def __init__(self):
self.d = {"foo": self.__class__.foo}
def bar(self):
self.d['foo'](self)
# Gets __func__ if it's an unbound method (2.x), leaves it alone if not (3.x)
self.d['foo'] = getattr(Child1.foo, '__func__', Child1.foo)
self.d['foo'](self)
class Child1(Parent):
def foo(self):
print("foo of class Child1: instance " + self.__class__.__name__)
class Child2(Parent):
pass
Child2().bar()
推荐阅读
- sql - SQL 按自定义顺序排序
- python - 在 Python 中将具有行/列索引的数据转换为表
- python - Python - 将数据转移到不同的行
- react-native - 卸载并移动屏幕后在 Audio.Sound() 上调用的方法
- r - 如何将 diff() 与 group_by() 结合使用
- jekyll - 使用分页时如何按修改日期对帖子进行排序?
- sql - SQL:根据最近的日期选择一个字段中的值是唯一的记录
- c# - StackLayout 中的 ChartView 截断图表
- angular - 您好,我在发出 http 删除请求时收到以下错误
- scala - Scala“无法在对象 CassandraConnector 中访问 DCAwareRoundRobinPolicy 类中的构造函数 DCAwareRoundRobinPolicy”