Python中的函数能否像类一样工作?

3 投票
2 回答
1999 浏览
提问于 2025-04-18 16:12

在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 个回答

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)
8

你可以把一个函数看作是一个只实现了 __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>

撰写回答