我想使用new.instancemethod将函数(aFunction)从一个类(A)分配给另一个类(B)的实例。我不知道如何让aFunction允许自己应用于B的实例 - 目前我收到一个错误,因为aFunction期望在A的实例上执行。
[注意:我不能使用__class__属性将实例强制转换为A,因为我正在使用的类(在我的示例中为B)不支持类型转换]
import new
class A(object):
def aFunction(self):
pass
#Class which doesn't support typecasting
class B(object):
pass
b = B()
b.aFunction = new.instancemethod(A.aFunction, b, B.__class__)
b.aFunction()
这是错误:
TypeError: unbound method aFunction() must be called with A instance as first argument (got B instance instead)
答案 0 :(得分:6)
new.instancemethod
需要一个功能。 A.aFunction
是一种未绑定的方法。那不是一回事。 (您可能想尝试添加一堆打印语句来显示所涉及的所有内容 - A.aFunction()
,A().aFunction
等 - 及其类型,以帮助理解。)
我假设你没有descriptors的工作方式。如果你不想学习所有的血腥细节,那么实际发生的是你在类定义中声明aFunction
会从函数中创建一个非数据描述符。这是一个神奇的事情,这意味着,根据您访问它的方式,您将获得未绑定的方法(A.aFunction
)或绑定的方法(A().aFunction
)。
如果您将aFunction
修改为@staticmethod
,这实际上会有效,因为对于静态方法,A.aFunction
和A().aFunction
都只是函数。 (我不确定标准会保证这是真的,但是很难看出其他人会如何实现它。)但如果你想要“aFunction允许自己应用于B的实例”,那么静态方法不会帮助你。
如果你真的想要获得底层函数,有很多方法可以做到;我认为这是帮助您理解描述符如何工作的最明确的:
f = object.__getattribute__(A, 'aFunction')
另一方面,最简单的可能是:
f = A.aFunction.im_func
然后,调用new.instancemethod
是如何将该函数转换为可以作为B类实例的常规方法调用的描述符:
b.aFunction = new.instancemethod(f, b, B)
打印出大量数据会让事情变得更加清晰:
import new
class A(object):
def aFunction(self):
print self, type(self), type(A)
#Class which doesn't support typecasting
class B(object):
pass
print A.aFunction, type(A.aFunction)
print A().aFunction, type(A().aFunction)
print A.aFunction.im_func, type(A.aFunction.im_func)
print A().aFunction.im_func, type(A().aFunction.im_func)
A.aFunction(A())
A().aFunction()
f = object.__getattribute__(A, 'aFunction')
b = B()
b.aFunction = new.instancemethod(f, b, B)
b.aFunction()
你会看到这样的事情:
<unbound method A.aFunction> <type 'instancemethod'>
<bound method A.aFunction of <__main__.A object at 0x108b82d10>> <type 'instancemethod'>
<function aFunction at 0x108b62a28> <type 'function'>
<function aFunction at 0x108b62a28> <type 'function'>
<__main__.A object at 0x108b82d10> <class '__main__.A'> <type 'type'>
<__main__.A object at 0x108b82d10> <class '__main__.A'> <type 'type'>
<__main__.B object at 0x108b82d10> <class '__main__.B'> <type 'type'>
唯一没有显示的是创建绑定和未绑定方法的魔力。为此,你需要研究A.aFunction.im_func.__get__
,那时你最好还是阅读描述符,而不是试图自己分开。
最后一件事:正如brandizzi指出的那样,这是你几乎不想做的事情。替代方案包括:将aFunction
写为自由函数而不是方法,因此您只需调用b.aFunction()
;将实际工作的功能分解出去,让A.aFunction
和B.aFunction
调用它;让B
聚合A
并转发给它;等
答案 1 :(得分:1)
我已经回答了你问的问题。但是你不应该达到理解new.instancemethod
来解决问题的程度。发生这种情况的唯一原因是因为你提出了错误的问题。让我们来看看应该提出的问题以及原因。
在PySide.QtGui中,我希望列表小部件项目具有设置字体和颜色的方法,但它们似乎没有。
这就是你真正想要的。可能有一种简单的方法可以做到这一点。如果是这样,那就是你想要的答案。此外,从这开始,您将避免所有关于“您真正想做什么?”的评论。或者“我怀疑这适用于你想要做的任何事情”(这通常伴随着投票 - 或者更重要的是,潜在的回答者只是忽略了你的问题)。
当然我可以编写一个接受QListWidgetItem并调用该函数的函数,而不是将其作为方法,但这对我不起作用,因为 _ _。
我认为有一个理由对你不起作用。但我真的不能想到一个好的。无论代码如何说:
item.setColor(color)
反而会这样说:
setItemColor(item, color)
很简单。
即使您需要(例如,使用绑定到其中的项目来传递颜色设置委托),使用方法的功能也几乎一样容易。而不是:
delegate = item.setColor
它是:
delegate = lambda color: setItemColor(item, color)
所以,如果 是一个很好的理由,你需要将它作为一种方法,这是你真正应该解释的。如果你很幸运,那么事实证明你错了,并且有一种更简单的方式来做你想要的而不是你想要的。
显而易见的方法是让PySide让我指定一个类或工厂函数,这样我就可以编写一个QListWidgetItem子类,而我处理过的每个列表项都是该子类的一个实例。但我找不到任何办法。
这似乎应该是PySide的一个功能。所以也许是,在这种情况下你想知道。如果不是,并且你或任何评论者或回答者都没有想到一个很好的理由,那就是糟糕的设计或难以实现,你应该提交一个功能请求,它可能在下一个版本中。 (如果您需要在下周针对当前版本发布代码,这不会对您有所帮助,但它仍然值得做。)
由于我找不到这样做的方法,我试图找到一些方法将
setColor
方法添加到QListWidgetItem
类,但无法想到任何内容。
Monkey-patching类非常简单:
QListWidgetItem.setColor = setItemColor
如果你不知道你能做到这一点,那没关系。如果人们知道你正在尝试做的事情,这将是他们建议的第一件事。 (好吧,也许没有多少Python程序员对猴子修补有很多了解,但它仍然比知道描述符或new.instancemethod
的数字要多得多。)而且,除了作为答案之外你还会更快,并且不那么麻烦,这是一个更好的答案。
即使您确实知道这一点,一些扩展模块也不会让您这样做。如果您尝试过但失败了,请解释一下不起作用的内容:
PySide不允许我将该方法添加到类中;当我尝试猴子修补时, _ _。
也许有人知道为什么它不起作用。如果没有,至少他们知道你试过了。
所以我必须将它添加到每个实例中。
Monkey-patching实例如下所示:
myListWidgetItem.setColor = setItemColor
再次,你会得到一个快速回答,一个更好的回答。
但是也许你知道这一点,并尝试过它,它也不起作用:
PySide也不允许我将方法添加到每个实例;当我尝试时, _ _。
所以我尝试修补每个实例的
__class__
,使其指向自定义子类,因为它在PyQt4中有效,但在PySide中它 _ _。
这可能不会让你有用,因为它只是PySide的工作方式。但值得一提的是你尝试过。
所以,我决定创建那个自定义子类,然后复制它的方法,就像这样,但 _ _。
在这里,一直到你把所有的东西放在你原来的问题中。如果确实有必要解决您的问题,那么信息就会存在。但是如果有一个简单的解决方案,你就不会从那些只是猜测new.instancemethod
如何工作的人那里得到一堆混乱的答案。
答案 2 :(得分:0)
如果可能,您可以使用aFunction
装饰器使@staticmethod
无约束: -
class A(object):
@staticmethod
def aFunction(B): # Modified to take appropriate parameter..
pass
class B(object):
pass
b = B()
b.aFunction = new.instancemethod(A.aFunction, b, B.__class__)
b.aFunction()
*注意: - 您需要修改方法以采取适当的参数..
答案 3 :(得分:0)
令我惊讶的是,这些答案都没有给出看似最简单的解决方案,特别是在您希望现有实例将方法 x 替换为来自另一个类(例如子类)的方法 x(同名)的情况下,“嫁接在它上面。
我在完全相同的上下文中遇到了这个问题,即 PyQt5。具体来说,使用 QTreeView
类,问题是我已经为树结构(第一列)中涉及的所有树项子类化了 QStandardItem
(称之为 MyItem
),等等事情,添加或删除这样的项目涉及一些额外的东西,特别是向字典添加键或从字典中删除键。
覆盖 appendRow
(以及 removeRow
、insertRow
、takeRow
等)没有问题:
class MyItem( QStandardItem ):
...
def appendRow( self, arg ):
# NB QStandarItem.appendRow can be called either with an individual item
# as "arg", or with a list of items
if isinstance( arg, list ):
for element in arg:
assert isinstance( element, MyItem )
self._on_adding_new_item( element )
elif isinstance( arg, MyItem ):
self._on_adding_new_item( arg )
else:
raise Exception( f'arg {arg}, type {type(arg)}')
super().appendRow( self, arg )
... 其中 _on_adding_new_item
是一种填充字典的方法。
当您想要添加“0 级”行时会出现问题,即其父行是 QTreeView
的“不可见根”。自然地,您希望这个新的“0 级”行中的项目为每个项目创建一个字典条目,但是如何获得这个不可见的根项目,类 QStandardItem
来做到这一点?
我尝试覆盖模型的方法 invisibleRootItem()
以提供不是 super().invisibleRootItem()
,而是一个新的 MyItem
。这似乎不起作用,可能是因为 QStandardItemModel.invisibleRootItem()
在幕后做事,例如设置项目的 model()
方法以返回 QStandardItemModel
。
解决方案非常简单:
class MyModel( QStandardItemModel ):
def __init__( self ):
self.root_item = None
...
...
def invisibleRootItem( self ):
# if this method has already been called we don't need to do our modification
if self.root_item != None:
return self.root_item
self.root_item = super().invisibleRootItem()
# now "graft" the method from MyItem class on to the root item instance
def append_row( row ):
MyItem.appendRow( self.root_item, row )
self.root_item.appendRow = append_row
... 然而,这还不够:super().appendRow( self, arg )
中的 MyItem.appendRow
然后在根项目上调用时会引发异常,因为 QStandardItem
没有 {{1} }.相反,super()
更改为:
MyItem.appendRow
答案 4 :(得分:-1)
感谢Rohit Jain - 这就是答案:
import new
class A(object):
@staticmethod
def aFunction(self):
pass
#Class which doesn't support typecasting
class B(object):
pass
b = B()
b.aFunction = new.instancemethod(A.aFunction, b, B.__class__)
b.aFunction()