223

没有多少人知道这个特性,但是 Python 的函数(和方法)可以有属性。看哪:

>>> def foo(x):
...     pass
...     
>>> foo.score = 10
>>> dir(foo)
['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', '__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name', 'score']
>>> foo.score
10
>>> foo.score += 1
>>> foo.score
11

这个特性在 Python 中有哪些可能的用途和滥用?我知道的一个很好的用途是PLY使用文档字符串将语法规则与方法相关联。但是自定义属性呢?有充分的理由使用它们吗?

4

8 回答 8

172

我通常使用函数属性作为注释的存储。假设我想写,C#的风格(表示某个方法应该是Web服务接口的一部分)

class Foo(WebService):
    @webmethod
    def bar(self, arg1, arg2):
         ...

然后我可以定义

def webmethod(func):
    func.is_webmethod = True
    return func

然后,当 web 服务调用到达时,我查找方法,检查底层函数是否具有 is_webmethod 属性(实际值无关),如果方法不存在或不打算通过 web 调用,则拒绝服务。

于 2008-12-03T18:06:51.230 回答
142

我将它们用作函数的静态变量。例如,给定以下 C 代码:

int fn(int i)
{
    static f = 1;
    f += i;
    return f;
}

我可以在 Python 中类似地实现该函数:

def fn(i):
    fn.f += i
    return fn.f
fn.f = 1

这肯定会落入频谱的“滥用”端。

于 2008-12-03T19:23:38.660 回答
60

你可以用 JavaScript 的方式做对象......这没有意义,但它有效;)

>>> def FakeObject():
...   def test():
...     print "foo"
...   FakeObject.test = test
...   return FakeObject
>>> x = FakeObject()
>>> x.test()
foo
于 2008-12-04T13:53:06.890 回答
16

我很少使用它们,但它们非常方便:

def log(msg):
   log.logfile.write(msg)

现在我可以log在整个模块中使用,只需通过设置来重定向输出log.logfile。有很多其他方法可以实现这一点,但这个轻巧且简单。虽然我第一次这样做时闻起来很有趣,但我开始相信它闻起来比使用全局logfile变量更好。

于 2009-10-07T18:20:06.360 回答
14

函数属性可用于编写将代码和相关数据包装在一起的轻量级闭包:

#!/usr/bin/env python

SW_DELTA = 0
SW_MARK  = 1
SW_BASE  = 2

def stopwatch():
   import time

   def _sw( action = SW_DELTA ):

      if action == SW_DELTA:
         return time.time() - _sw._time

      elif action == SW_MARK:
         _sw._time = time.time()
         return _sw._time

      elif action == SW_BASE:
         return _sw._time

      else:
         raise NotImplementedError

   _sw._time = time.time() # time of creation

   return _sw

# test code
sw=stopwatch()
sw2=stopwatch()
import os
os.system("sleep 1")
print sw() # defaults to "SW_DELTA"
sw( SW_MARK )
os.system("sleep 2")
print sw()
print sw2()

1.00934004784

2.00644397736

3.01593494415

于 2008-12-03T20:27:41.853 回答
11

我创建了这个辅助装饰器来轻松设置函数属性:

def with_attrs(**func_attrs):
    """Set attributes in the decorated function, at definition time.
    Only accepts keyword arguments.
    E.g.:
        @with_attrs(counter=0, something='boing')
        def count_it():
            count_it.counter += 1
        print count_it.counter
        print count_it.something
        # Out:
        # >>> 0
        # >>> 'boing'
    """
    def attr_decorator(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            return fn(*args, **kwargs)

        for attr, value in func_attrs.iteritems():
            setattr(wrapper, attr, value)

        return wrapper

    return attr_decorator

一个用例是创建工厂集合并查询它们可以在函数元级别创建的数据类型。
例如(非常愚蠢的一个):

@with_attrs(datatype=list)
def factory1():
    return [1, 2, 3]

@with_attrs(datatype=SomeClass)
def factory2():
    return SomeClass()

factories = [factory1, factory2]

def create(datatype):
    for f in factories:
        if f.datatype == datatype:
            return f()
    return None
于 2015-03-26T18:50:05.743 回答
6

有时我使用函数的属性来缓存已经计算的值。您还可以拥有一个泛化这种方法的通用装饰器。请注意此类函数的并发问题和副作用!

于 2008-12-04T08:43:52.723 回答
1

我一直认为这可能的唯一原因是有一个合乎逻辑的地方可以放置文档字符串或其他类似的东西。我知道如果我将它用于任何生产代码,它会使大多数阅读它的人感到困惑。

于 2009-01-16T18:25:23.187 回答