普通函数中的"self"?

8 投票
11 回答
3875 浏览
提问于 2025-04-16 01:00

我有一堆函数(不在任何类里面),我给它们设置了一些属性,比如 funcname.fields = 'xxx'。我本来希望能在函数内部用 self.fields 来访问这些变量,但当然它告诉我:

全局名称 'self' 未定义

那么...我该怎么办呢?有没有什么神奇的变量可以访问?比如 __this__.fields


有些人问“为什么要这样做?”你可能会不同意我的理由,但我有一组函数,它们都必须有相同的参数格式(只接受一个参数)。大部分情况下,这一个参数就足够完成需要的计算了。不过在一些特定情况下,还需要一些额外的信息。与其强迫每个函数接受一长串大部分没用的变量,我决定直接在函数上设置这些属性,这样就可以轻松忽略它们。

不过,我现在想到了,如果你不在乎额外的参数,可以把 **kwargs 作为最后一个参数传进去。唉...算了...

编辑:其实,有些函数是我没有写的,我也不想修改它们来接受额外的参数。通过把额外的参数作为属性“传入”,我的代码可以同时与那些利用额外参数的自定义函数和不需要额外参数的第三方代码一起工作。

谢谢大家的快速回复 :)

11 个回答

3
def foo():
    print(foo.fields)
foo.fields=[1,2,3]

foo()
# [1, 2, 3]

给函数添加属性是完全没问题的。很多记忆化函数(memoizers)就是利用这个特性在函数内部缓存结果。

比如,看看这个 func.cache 的用法:

from decorator import decorator
@decorator
def memoize(func, *args, **kw):
    # Author: Michele Simoniato
    # Source: http://pypi.python.org/pypi/decorator
    if not hasattr(func, 'cache'):
        func.cache = {}
    if kw: # frozenset is used to ensure hashability
        key = args, frozenset(kw.iteritems())
    else:
        key = args
    cache = func.cache # attribute added by memoize
    if key in cache:
        return cache[key]
    else:
        cache[key] = result = func(*args, **kw)
        return result
13

self 其实不是 Python 的关键字,它只是一个普通的变量名。在创建实例方法的时候,你可以把第一个参数命名成任何你想要的名字,self 只是一个约定俗成的写法。

通常情况下,传递参数给函数比设置属性作为输入要更好,但如果你真的需要这样做,可以用函数的名字来访问函数内部的变量:

def a:
    if a.foo:
        #blah

a.foo = false
a()

想了解更多关于这方面的内容,可以看看 python 函数属性的使用与误用。:D

2

你不能在所有情况下都正确地让“函数访问自己的属性”——想了解更多可以看看这里 如何让Python函数访问自己的属性?——不过这里有个简单的演示:

>>> def f(): return f.x
... 
>>> f.x = 7
>>> f()
7
>>> g = f
>>> g()
7
>>> del f
>>> g()
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "<interactive input>", line 1, in f
NameError: global name 'f' is not defined

基本上,大多数方法都是通过名字在全局范围内查找来直接或间接访问函数对象的;如果原来的函数名被删除了,这个方法就不管用了。还有其他一些笨拙的方法可以实现这个,比如定义类或者工厂,但根据你的解释,显然你并不需要这些。

只需使用提到的关键字捕获所有参数,像这样:

def fn1(oneArg):
    // do the due

def fn2(oneArg, **kw):
    if 'option1' in kw:
        print 'called with option1=', kw['option1']
    //do the rest

fn2(42)
fn2(42, option1='something')

不太明白你在评论中提到的处理TypeError是什么意思——使用**kw时不会出现这个问题。这种方法在一些Python系统函数中效果很好——比如min()、max()、sort()。最近在CodeGolf挑战中,sorted(dct,key=dct.get,reverse=True)对我帮助很大 :)

撰写回答