Python函数属性 – 使用和滥用

没有多less人知道这个特性,但是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使用docstring将语法规则与方法相关联。 但是,自定义属性呢? 有没有很好的理由使用它们?

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

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

那么我可以定义

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

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

我用它们作为函数的静态variables。 例如,给出以下C代码:

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

我可以在Python中同样实现这个function:

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

这肯定会落入这个“滥用”的范畴。

你可以用JavaScript的方式做对象…这是没有道理的,但它的工作原理;)

 >>> def FakeObject(): ... def test(): ... print "foo" ... FakeObject.test = test ... return FakeObject >>> x = FakeObject() >>> x.test() foo 

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

 #!/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

我谨慎地使用它们,但它们可以非常方便:

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

现在我可以在整个模块中使用log ,并简单地通过设置log.logfileredirect输出。 有很多很多其他的方法来完成,但这个轻量级和污垢简单。 我第一次听到它的时候闻起来很有趣,但我认为它比全局的logfilevariables更好闻。

有时我使用函数的属性来caching已经计算的值。 你也可以有一个通用的装饰器来概括这种方法。 注意并发问题和这些function的副作用!

我一直认为这是可能的唯一原因是所以有一个合理的地方放置一个文档string或其他类似的东西。 我知道如果我把它用于任何生产代码,会让大多数读者感到困惑。

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

 def with_attrs(**func_attrs): """Set attributes in the decorated function, at definition time. Only accepts keyword arguments. Eg: @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 

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

 @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