动态添加的函数可以访问python中的所有者对象吗?

时间:2018-03-07 01:43:45

标签: python

我正在python中创建一个程序,其中对象的特定实例必须使用在运行时构建的新函数进行修饰。

我已经看到通过MethodType向对象添加函数的非常简单的示例:

import types
def foo():
    print("foo")

class A:
    bar = "bar"

a = A()
a.foo = types.MethodType(foo, a)

但我见过的所有例子都没有显示以这种方式添加的函数如何引用新所有者的属性。据我所知,即使这会将foo()函数绑定到实例afoo()仍必须是纯函数,并且不能包含对本地任何内容的引用。

在我的情况下,我需要函数来更改它们被添加到的对象的属性。以下是我需要做的事情的两个例子:

class A:
    foo = "foo"
    def printme():
        print(foo)

def nofoo():
    foo = "bar"

def printBar():
    if foo != "foo"
        self.printme()

然后我需要一种方法将nofoo()printBar()的副本添加到A对象,以便它们可以访问名为foo的对象属性和函数正确命名为printme()

那么,这可能吗? 有没有办法在vanilla Python中进行这种编程?或者至少是否有编程模式可以实现这种行为?

P.S。:在我的系统中,我还动态地向对象添加属性。您的第一个想法可能是“我怎么能确定我添加nofoo()函数的对象实际上有一个名为foo的属性?”,但我也有一个相当强大的标记系统这确保我从不尝试将nofoo()函数添加到没有foo变量的对象。我提到这个的原因是,查看类定义的解决方案对我来说并不是很有用。

3 个答案:

答案 0 :(得分:1)

如评论中所述,您的函数实际上必须至少采用一个参数:self,该方法被调用的实例。可以使用self参数,因为它将在普通实例方法中使用。这是一个例子:

>>> from types import MethodType
>>> 
>>> class Class:
        def method(self):
            print('method run')


>>> cls = Class()
>>> 
>>> def func(self): # must accept one argument, `self`
        self.method()


>>> cls.func = MethodType(func, cls)
>>> cls.func()
method run
>>>

如果您的函数不接受self,则会引发异常:

>>> def func():
        self.method()

>>> cls.func = MethodType(func, cls)
>>> cls.func()
Traceback (most recent call last):
  File "<pyshell#21>", line 1, in <module>
    cls.func()
TypeError: func() takes 0 positional arguments but 1 was given
>>> 

答案 1 :(得分:0)

class A:
     def __init__(self):
         self.foo = "foo"

     def printme(self):
         print(self.foo)

def nofoo(self):
     self.foo = "bar"

a.nofoo = types.MethodType(nofoo, a)
a.nofoo()
a.printme()

打印

bar

答案 2 :(得分:0)

目前还不完全清楚你要做什么,而且我担心无论它是什么都可能是一个坏主意。但是,我可以解释如何做你正在问的事情,即使它不是你想要的或者想要的。我要指出,想要在下面做第二个版本是非常罕见的,甚至更少想要做第三个版本,但Python确实允许它们两个,因为“甚至比非常罕见”还不是“从不” ”。而且,本着同样的精神......

简短的回答是“是”。动态添加的方法可以完全按照常规方法的方式访问所有者对象。

首先,这是一个正常的非动态方法:

class C:
    def meth(self):
        return self.x

c = C()
c.x = 3
c.meth()

显然,使用这样的常规方法,当您致电c.meth()时,c最终会成为self参数的值,因此self.x为{{ 1}},即3。

现在,以下是动态向类添加方法的方法:

c.x

这实际上是做同样的事情。 (好吧,我们为同一个函数对象留下了另一个名字,但这是唯一的区别)如果class C: pass c = C() c.x = 3 def meth(self): print(self.x) C.meth = meth c.meth() 与第一个版本中的函数相同,那么显然无论魔法是什么{{1在第一个版本中工作将完成同样的事情。

(这在Python 2中稍微复杂一些,因为没有绑定方法,也有经典类......但幸运的是你不必担心这个。)

最后,以下是动态向实例添加方法的方法:

C.meth

在这里,你实际上必须知道在前两种情况下使c.meth()工作的魔力。请阅读Descriptor HOWTO。在那之后,它应该是显而易见的。

但是如果你只是想假装Guido是一个巫师(Raymond 肯定是一个巫师)并且它是魔术......那么,在前两个版本中,Guido的魔杖会创建一个特殊的绑定方法对象无论何时你要求class C: pass c = C() c.x = 3 def meth(self): print(self.x) c.meth = types.MethodType(meth, c) c.meth() ,但是当c.meth()不存在时,即使他不够神奇。但是我们可以煞费苦心地创建相同的绑定方法对象并将其存储为c.meth。在那之后,每当我们要求C.meth时,我们都会得到同样的东西,我们明确地将它们构建为前两个示例中的相同内容,因此它显然会做同样的事情。

但如果我们这样做会怎么样:

c.meth

在这里,你不是让Guido做他的描述符魔术来创建c.meth,你不是手动做的,你只是在那里坚持常规功能。这意味着如果你想要任何东西显示为class C: pass c = C() c.x = 3 def meth(self): print(self.x) c.meth = meth c.meth(c) 参数,你必须明确地将它作为参数传递,就像最后那条愚蠢的c.meth行一样。但是如果你愿意这样做,那么即使这个也可以。无论self最终如何c.meth(c)self将成为c