调用保存在 class 属性中的函数:内置函数与普通函数的不同行为
calling a function saved in a class attribute: different behavior with built-in function vs. normal function
使用以下脚本:
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
都是采用单个参数的函数,那么为什么这两个脚本的行为不同?
- 如何安全地将普通函数保存在 class/instance 属性中并稍后从实例方法中调用它?
如果您希望将函数存储为实例属性:
class Foo(object):
def __init__(self):
self.func = tmp
def go(self):
return self.func(0.0)
您需要在实例的上下文中分配函数,在初始化程序中这样做是一个可能的地方。
希望对您有所帮助。
注意区别...:
>>> import time
>>> type(time.gmtime)
<type 'builtin_function_or_method'>
>>> def f(*a): return time.gmtime(*a)
...
>>> type(f)
<type 'function'>
A <type 'function'>
(这是一个描述符)不是与<type 'builtin_function_or_method'>
(不是)相同的东西...
你问"How can I safely save an ordinary function in a class/instance attribute and call it later from an instance method?".
如果 "an ordinary function" 是指 <type 'function'>
,则将其包装为 staticmethod
以避免 self
在调用时作为第一个参数插入。而且,也可以将 staticmethod
包裹在内置的周围。所以我想这是你最好的选择!
- Both
time.gmtime
and tmp
are functions that take a single argument, so why do the two scripts behave differently?
让我们了解当您
时Python会做什么
self.func
When an instance attribute is referenced that isn’t a data attribute, its class is searched. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object. When the method object is called with an argument list, a new argument list is constructed from the instance object and the argument list, and the function object is called with this new argument list.
因此,如果 func
是 self
中的有效 函数对象 ,则将创建一个绑定方法对象,它期望第一个参数为是调用此函数的对象。
现在让我们比较一下,
print type(time.gmtime)
class Foo(object):
func = time.gmtime
def go(self):
print type(self.func)
return self.func(0.0)
当用Foo().go()
调用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
的对象作为第一个参数。因此,tmp
实际上作为 instancemethod
绑定到 Foo
。当你像 Foo().go()
那样调用 go
时,它会像这样
在内部调用 tmp
Foo.func(self, 0.0)
有效
tmp(self, 0.0)
这就是您收到以下错误的原因
TypeError: tmp() takes exactly 1 argument (2 given)
- How can I safely save an ordinary function in a class/instance attribute and call it later from an instance method?
方案一:
再次引用Python documentation,
It is also important to note that user-defined functions which are attributes of a class instance are not converted to bound methods; this only happens when the function is an attribute of the class.
这意味着,当您将用户定义的函数分配给 class 变量时,将发生绑定方法构造。但是,如果您将它分配给一个实例,那么它就不会发生。
所以,你可以利用它来发挥你的优势,就像这样
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)
,因为没有绑定方法构造发生。
方案二:
像这样使用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
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)
使用以下脚本:
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
都是采用单个参数的函数,那么为什么这两个脚本的行为不同?- 如何安全地将普通函数保存在 class/instance 属性中并稍后从实例方法中调用它?
如果您希望将函数存储为实例属性:
class Foo(object):
def __init__(self):
self.func = tmp
def go(self):
return self.func(0.0)
您需要在实例的上下文中分配函数,在初始化程序中这样做是一个可能的地方。
希望对您有所帮助。
注意区别...:
>>> import time
>>> type(time.gmtime)
<type 'builtin_function_or_method'>
>>> def f(*a): return time.gmtime(*a)
...
>>> type(f)
<type 'function'>
A <type 'function'>
(这是一个描述符)不是与<type 'builtin_function_or_method'>
(不是)相同的东西...
你问"How can I safely save an ordinary function in a class/instance attribute and call it later from an instance method?".
如果 "an ordinary function" 是指 <type 'function'>
,则将其包装为 staticmethod
以避免 self
在调用时作为第一个参数插入。而且,也可以将 staticmethod
包裹在内置的周围。所以我想这是你最好的选择!
- Both
time.gmtime
andtmp
are functions that take a single argument, so why do the two scripts behave differently?
让我们了解当您
时Python会做什么self.func
When an instance attribute is referenced that isn’t a data attribute, its class is searched. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object. When the method object is called with an argument list, a new argument list is constructed from the instance object and the argument list, and the function object is called with this new argument list.
因此,如果 func
是 self
中的有效 函数对象 ,则将创建一个绑定方法对象,它期望第一个参数为是调用此函数的对象。
现在让我们比较一下,
print type(time.gmtime)
class Foo(object):
func = time.gmtime
def go(self):
print type(self.func)
return self.func(0.0)
当用Foo().go()
调用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
的对象作为第一个参数。因此,tmp
实际上作为 instancemethod
绑定到 Foo
。当你像 Foo().go()
那样调用 go
时,它会像这样
tmp
Foo.func(self, 0.0)
有效
tmp(self, 0.0)
这就是您收到以下错误的原因
TypeError: tmp() takes exactly 1 argument (2 given)
- How can I safely save an ordinary function in a class/instance attribute and call it later from an instance method?
方案一:
再次引用Python documentation,
It is also important to note that user-defined functions which are attributes of a class instance are not converted to bound methods; this only happens when the function is an attribute of the class.
这意味着,当您将用户定义的函数分配给 class 变量时,将发生绑定方法构造。但是,如果您将它分配给一个实例,那么它就不会发生。
所以,你可以利用它来发挥你的优势,就像这样
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)
,因为没有绑定方法构造发生。
方案二:
像这样使用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
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)