3

我想使用 new.instancemethod 将一个类 (A) 中的函数 (aFunction) 分配给另一个类 (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)
4

5 回答 5

6

new.instancemethod接受一个功能。A.aFunction是一个未绑定的方法。那不是一回事。(您可能想尝试添加一堆打印语句来显示所有涉及的内容 - <code>A.aFunction()A().aFunction等 - 以及它们的类型,以帮助理解。)

我假设你不知道描述符是如何工作的。如果您不想了解所有血淋淋的细节,那么实际发生的情况是您aFunction在类定义中的声明会在函数之外创建一个非数据描述符。这是一个神奇的东西,这意味着,根据你访问它的方式,你会得到一个未绑定的方法(A.aFunction)或一个绑定的方法(A().aFunction)。

如果您修改aFunction为 a @staticmethod,这实际上会起作用,因为对于静态方法来说,两者A.aFunctionA().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__,此时您最好阅读描述符 howto 而不是尝试自己挖掘它。

最后一件事:正如brandizzi 指出的那样,这是您几乎不想做的事情。替代方案包括: 编写aFunction为自由函数而不是方法,因此您只需调用b.aFunction(); 分解出一个真正起作用的函数,然后A.aFunction调用B.aFunction它;B聚合一个并A转发给它;等等

于 2012-09-27T19:15:45.587 回答
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类中,但想不出任何东西。

猴子修补类非常简单:

QListWidgetItem.setColor = setItemColor

如果你不知道你可以做到这一点,那很好。如果人们知道这就是你想要做的,这将是他们建议的第一件事。(好吧,也许没有多少 Python 程序员对猴子补丁了解很多,但它仍然比了解描述符或new.instancemethod.更好的答案。

即使您确实知道这一点,某些扩展模块也不会让您这样做。如果您尝试过但失败了,请解释什么不起作用:

PySide 不允许我将方法添加到类中;当我尝试修补猴子时,_ _。

也许有人知道为什么它不起作用。如果没有,至少他们知道你试过了。

所以我必须将它添加到每个实例中。

猴子修补实例如下所示:

myListWidgetItem.setColor = setItemColor

同样,您会得到一个快速的答案,而且是一个更好的答案。

但也许你知道这一点,并尝试过,但它也没有奏效:

PySide 也不允许我将方法添加到每个实例;当我尝试时,_ _。

所以我尝试修补__class__每个实例,使其指向一个自定义子类,因为这在 PyQt4 中有效,但在 PySide 中它_ _。

这可能不会为您带来任何有用的东西,因为这正是 PySide 的工作方式。但值得一提的是你试过了。

所以,我决定创建那个自定义子类,然后复制它的方法,就像这样,但是_

一直到这里,您将在原始问题中放入所有内容。如果确实有必要解决您的问题,那么信息就在那里。但是,如果有一个简单的解决方案,您就不会从那些只是猜测其工作原理的人那里得到一堆令人困惑的答案new.instancemethod

于 2012-09-28T03:28:50.710 回答
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()

*注意: - 您需要修改方法以采用适当的参数..

于 2012-09-27T18:54:40.083 回答
0

我很惊讶这些答案都没有给出看似最简单的解决方案,特别是在您希望现有实例通过“嫁接”而将方法 x 替换为来自另一个类(例如子类)的方法 x(同名)时给它。

我在相同的上下文中遇到了这个问题,即使用 PyQt5。具体来说,使用一个QTreeView类,问题是我已经对树结构(第一列)中涉及的所有树项进行了子类化QStandardItem(调用它MyItem),除其他外,添加或删除这样一个项目涉及一些额外的东西,特别是添加字典的键或从该字典中删除键。

覆盖appendRow(and 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. 自然地,您希望这个新的“level-0”行中的项目为每个项目创建一个字典条目,但是如何获得这个不可见的根项目 classQStandardItem呢?

我尝试覆盖模型的方法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 )在根项上调用 inMyItem.appendRow时会引发异常,因为 aQStandardItem没有super(). 相反,MyItem.appendRow改为:

def appendRow( self, arg ):
    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)}')
    QStandardItem.appendRow( self, arg )
于 2021-01-09T10:22:49.333 回答
-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()
于 2012-09-27T18:55:36.267 回答