我正在使用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这个函数是一个静态函数(不要将对象作为第一个参数传递)?
答案 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.myfn
和MyClass().myfn
都将提供与fn
相同的函数对象,这与在执行类时分配给myfn
的对象不同宣言;该对象仅用于在构造类时将myfn
标记为静态。