说服Python将保存的函数作为静态

时间:2015-12-09 17:20:35

标签: python python-2.7

我正在使用Python 2.7(无法升级)。我正在尝试在我的类中存储一个函数,该函数可以在ctor中用不同的函数覆盖。该函数是一个普通的静态函数(不是成员或类方法),因此我不想在调用它时将类或对象作为第一个参数传递。出于某种原因,Python总是将对象作为第一个参数传递。我该如何避免这种情况?

示例:

def fn(*args):
    print str(args)

class MyClass(object):
    myfn = fn

    def __init__(self, myfn=None):
        if myfn is not None:
            self.myfn = myfn

    def run(self, arg):
        self.myfn(arg)


o = MyClass()
o.run("foo")

当我运行它时,我将对象作为fn的第一个参数:

$ python /tmp/testfn.py
(<__main__.MyClass object at 0x7f7fff8dc090>, 'foo')

如果我不添加类成员myfn而只是在ctor中分配值,那么它会按预期工作:

    def __init__(self, myfn=None):
        self.myfn = fn if myfn is None else myfn

$ python /tmp/testfn.py
('foo',)

但是,我不想这样做,因为我希望能够重置整个类的函数,以便所有未来的对象自动获得新函数,此外还能够为单个对象重置它。

我不明白为什么类成员值的行为不同。有什么方法可以说服python这个函数是一个静态函数(不要将对象作为第一个参数传递)?

2 个答案:

答案 0 :(得分:4)

  

有什么方法可以说服python这个函数是一个静态函数(不要把对象作为第一个参数传递)?

当然,告诉python它是staticmethod

class MyClass(object):
    myfn = staticmethod(fn)

演示:

>>> def fn(*args):
...     print str(args)
...
>>> class MyClass(object):
...     myfn = staticmethod(fn)
...     def run(self, arg):
...       self.myfn(arg)
... 
>>> o = MyClass()
>>> o.run('foo')
('foo',)

答案 1 :(得分:2)

原因是:在执行类定义之前,为类命名空间创建一个dict,并且赋值(如myfn=fn)进入该dict。函数定义(成员函数)创建普通函数对象,对它们的引用也放在同一个dict中。

当在一个实例上调用一个方法,并且属性查找(例如obj.method)解析为类dict中的一个函数时,它被视为一个方法调用 - 属性查找实际上构造了一个bound-method object,它将实例绑定到方法,以便在调用实际函数之前调用 对象插入额外的self参数。

标记为@staticmethod的函数的处理方式不同 - 当它们最初放在类名称空间中时,它们周围有一个包装器,当构造类时,这些包装器被删除,但是有一些记录被创建在类对象中,当它们被查找为实例的属性时阻止它们被转换为bound-methods。

因此,类中myfn的赋值给出与定义类中函数相同的结果 - 它成为类中的成员函数。以这种方式复制成员是很常见的:

class xyz:
    def __repr__(self):
          # ....
    __str__ = __repr__

如果在类定义之后放置MyClass.myfn = fn (不缩进),它仍然会起作用,如果myfn是该类的方法。

因此,@staticmethod是制作静态方法的常用方法。但是staticmethod本身就是一个函数:所以如果你所拥有的函数实际上已经在类之外定义了,你可以这样做:

class MyClass:
    myfn = staticmethod(fn)

...因为它与将@staticmethod放在函数之前具有相同的效果 - 新创建的函数只传递给staticmethod,结果放在命名空间中。在上面,MyClass.myfnMyClass().myfn都将提供与fn相同的函数对象,这与在执行类时分配给myfn的对象不同宣言;该对象仅用于在构造类时将myfn标记为静态。