使用以下脚本:
import time
class Foo(object):
func = time.gmtime
def go(self):
return self.func(0.0)
print time.strftime('%Y', Foo().go())
我得到以下输出:
1970
但是,如果我稍作修改并换行time.gmtime
:
import time
def tmp(stamp):
return time.gmtime(stamp)
class Foo(object):
func = tmp
def go(self):
return self.func(0.0)
print time.strftime('%Y', Foo().go())
然后我收到以下错误:
Traceback (most recent call last):
File "./test.py", line 13, in <module>
print time.strftime('%Y', Foo().go())
File "./test.py", line 11, in go
return self.func(0.0)
TypeError: tmp() takes exactly 1 argument (2 given)
显然,它正试图调用Foo.func
,好像它是一个实例方法,并将self
作为第一个参数传递。
两个问题:
time.gmtime
和tmp
都是采用单个参数的函数,那么为什么这两个脚本的行为不同?答案 0 :(得分:4)
- 醇>
time.gmtime
和tmp
都是采用单个参数的函数,那么为什么这两个脚本的行为不同?
让我们了解Python做什么
self.func
当引用的实例属性不是数据属性时,将搜索其类。如果名称表示有效的类属性是一个函数对象,则通过打包(指向)实例对象和刚在抽象对象中找到的函数对象来创建方法对象:这是方法对象。当使用参数列表调用方法对象时,将从实例对象和参数列表构造新的参数列表,并使用此新参数列表调用函数对象。
因此,如果func
是self
中的有效函数对象,那么将创建一个绑定的方法对象,该对象期望第一个参数成为其上的对象调用此函数。
现在让我们进行比较,
print type(time.gmtime)
class Foo(object):
func = time.gmtime
def go(self):
print type(self.func)
return self.func(0.0)
使用go
调用Foo().go()
时,会打印
<type 'builtin_function_or_method'>
<type 'builtin_function_or_method'>
没错。由于time.gmtime
是内置函数(与函数对象不同),因此此处没有创建绑定方法对象。让我们现在尝试你的第二个例子,
def tmp(stamp):
return time.gmtime(stamp)
print type(tmp), tmp
class Foo(object):
func = tmp
def go(self):
print type(self.func), self.func
return self.func(0.0)
会打印
<type 'function'> <function tmp at 0x7f34d9983578>
<type 'instancemethod'> <bound method Foo.tmp of <__main__.Foo object at ...>>
由于tmp
是一个函数对象,根据上面显示的文档,创建了一个绑定的方法对象,它希望Foo
的Object成为第一个参数。因此,tmp
实际上与Foo
绑定为instancemethod
。当您go
调用Foo().go()
时,会在内部调用此tmp
Foo.func(self, 0.0)
实际上是
tmp(self, 0.0)
这就是您收到以下错误的原因
TypeError: tmp() takes exactly 1 argument (2 given)
- 如何安全地将普通函数保存在类/实例属性中,稍后从实例方法调用它?
醇>
解决方案1:
再次引用Python documentation,
同样重要的是要注意,作为类实例属性的用户定义函数不会转换为绑定方法;只有在函数是类的属性时才会发生这种情况。
这意味着,当您将用户定义的函数分配给类变量时,将发生绑定的方法构造。但是,如果将其分配给实例,则不会发生。
所以,你可以利用这个优势,比如
import time
def tmp(stamp):
return time.gmtime(stamp)
class Foo(object):
def __init__(self):
self.func = tmp # Instance variable, not a class variable
def go(self):
return self.func(0.0)
print time.strftime('%Y', Foo().go())
此处,self.func
将转换为tmp(0.0)
,因为没有绑定方法构造发生。
解决方案2:
使用staticmethod
这样的功能
class Foo(object):
func = staticmethod(tmp)
def go(self):
# `return Foo.func(0.0)` This will also work
return self.func(0.0)
现在,self.func
仍会引用tmp
。它类似于像这样定义你的类
class Foo(object):
@staticmethod
def func(stamp):
return time.gmtime(stamp)
def go(self):
# `return Foo.func(0.0)` This will also work
return self.func(0.0)
答案 1 :(得分:2)
请注意差异......:
>>> import time
>>> type(time.gmtime)
<type 'builtin_function_or_method'>
>>> def f(*a): return time.gmtime(*a)
...
>>> type(f)
<type 'function'>
<type 'function'>
(是一个描述符)不与<type 'builtin_function_or_method'>
相同(不是......)
你问&#34;如何安全地将普通函数保存在类/实例属性中,稍后从实例方法中调用它?&#34;。
如果通过&#34;普通功能&#34;你的意思是<type 'function'>
,然后将其包装为staticmethod
,以避免self
作为第一个参数被调用时插入staticmethod
。而且,将{{1}}包裹在内置的内容也可以。所以我想这是你最好的选择!
答案 2 :(得分:0)
如果要将函数存储为实例属性:
class Foo(object):
def __init__(self):
self.func = tmp
def go(self):
return self.func(0.0)
您需要在实例的上下文中分配函数,在初始化程序中这样做是可能的。
希望有所帮助。