Python中的函数能否像类一样工作?
在Python中,函数被视为一种“第一类对象”。这意味着你可以像调用类一样调用函数。所以,你可以用类来替代函数。但是,你能让一个函数像类一样工作吗?比如说,能不能给函数添加或删除属性,或者调用内部的函数(这时叫做方法)呢?
我通过查看代码找到了实现这个的办法。
import inspect
class AddOne(object):
"""class definition"""
def __init__(self, num):
self.num = num
def getResult(self):
"""
class method
"""
def addOneFunc(num):
"inner function"
return num + 1
return addOneFunc(self.num);
if __name__ == '__main__':
two = AddOne(1);
two_src = '\n'.join([line[4:] for line in inspect.getsource(AddOne.getResult).split('\n')])
one_src = '\n'.join([line[4:] for line in two_src.split('\n')
if line[:4] == ' ' and line[4:8] == ' ' or line[4:8] == 'def '])
one_co = compile(one_src, '<string>', 'exec')
exec one_co
print addOneFunc(5)
print addOneFunc.__doc__
不过,有没有更直接的方法来访问类里面定义的局部变量和函数呢?
编辑
这个问题是关于如何深入了解Python内部结构的。当然,我在正常编程中不会这样做。这个问题是在我们讨论Python中的私有变量时产生的。我认为这与语言的哲学相悖。于是,有人举了上面的例子。目前看来,他的观点是对的。你不能在不使用inspect模块的情况下访问函数内部的函数,这使得这个函数变得私有。通过co_varnames我们已经非常接近了,因为我们已经得到了函数的名字。但是,存放这个名字的命名空间字典在哪里呢?如果你尝试使用
getResult.__dict__
它是空的。我希望能从Python得到这样的答案:
function addOneFunc at <0xXXXXXXXXX>
2 个回答
不确定下面的内容是否是你想要的,但你可以这样做:
>>> def f(x):
... print(x)
...
>>> f.a = 1
>>> f.a
1
>>> f(54)
54
>>> f.a = f
>>> f
<function f at 0x7fb03579b320>
>>> f.a
<function f at 0x7fb03579b320>
>>> f.a(2)
2
你可以给一个函数添加属性,这些属性可以是变量或者其他函数(注意,f.a = f
是为了简单起见选择的;当然你可以把f.a
赋值为任何函数)。
如果你想访问函数内部的局部变量,那就会比较复杂,可能确实需要用到一些反射的技巧。下面的例子使用了func_code
这个属性:
>>> def f(x):
... a = 1
... return x * a
...
>>> f.func_code.co_nlocals
2
>>> f.func_code.co_varnames
('x', 'a')
>>> f.func_code.co_consts
(None, 1)
你可以把一个函数看作是一个只实现了 __call__
的类的实例,也就是说:
def foo(bar):
return bar
大致上等同于:
class Foo(object):
def __call__(self, bar):
return bar
foo = Foo()
函数实例有一个 __dict__
属性,所以你可以随意给它们添加新的属性。
给一个函数添加属性可以用来实现一个记忆化装饰器,这个装饰器可以缓存函数之前的调用结果:
def memo(f):
@functools.wraps(f)
def func(*args):
if args not in func.cache: # access attribute
func.cache[args] = f(*args)
return func.cache[args]
func.cache = {} # add attribute
return func
注意,这个属性也可以在函数内部访问,尽管它必须在函数定义之后才能被定义。
所以你可以这样做:
>>> def foo(baz):
def multiply(x, n):
return x * n
return multiply(foo.bar(baz), foo.n)
>>> def bar(baz):
return baz
>>> foo.bar = bar
>>> foo.n = 2
>>> foo('baz')
'bazbaz'
>>> foo.bar = len
>>> foo('baz')
6
(不过可能没有人会因此感谢你!)
不过要注意,multiply
这个没有被作为 foo
的属性的函数,在函数外部是无法访问的:
>>> foo.multiply(1, 2)
Traceback (most recent call last):
File "<pyshell#20>", line 1, in <module>
foo.multiply(1, 2)
AttributeError: 'function' object has no attribute 'multiply'
另一个问题正好涉及你想要做的事情:
>>> import inspect
>>> import new
>>> class AddOne(object):
"""Class definition."""
def __init__(self, num):
self.num = num
def getResult(self):
"""Class method."""
def addOneFunc(num):
"inner function"
return num + 1
return addOneFunc(self.num)
>>> two = AddOne(1)
>>> for c in two.getResult.func_code.co_consts:
if inspect.iscode(c):
print new.function(c, globals())
<function addOneFunc at 0x0321E930>