Python函数属性-使用和滥用

Python函数属性-使用和滥用,python,function,attributes,Python,Function,Attributes,没有多少人知道这一特性,但是Python的函数(和方法)可以有。瞧: Python中此功能的可能用途和滥用是什么?我知道的一个很好的用法是使用docstring将语法规则与方法关联起来。但是自定义属性呢?有充分的理由使用它们吗?我通常使用函数属性作为注释的存储。假设我想以C#的风格编写(表示某个方法应该是web服务接口的一部分) 然后我可以定义 def webmethod(func): func.is_webmethod = True return func 然后,当webse

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


Python中此功能的可能用途和滥用是什么?我知道的一个很好的用法是使用docstring将语法规则与方法关联起来。但是自定义属性呢?有充分的理由使用它们吗?

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

然后我可以定义

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

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

我将它们用作函数的静态变量。例如,给定以下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

这肯定属于“滥用”范畴。

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

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

有时我使用函数的属性来缓存已计算的值。您还可以有一个泛型装饰器来概括这种方法。请注意这些函数的并发问题和副作用

您可以用JavaScript的方式处理对象。。。这毫无意义,但它是有效的;)


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

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

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

现在我可以在整个模块中使用
log
,只需设置
log.logfile
即可重定向输出。有很多其他的方法来实现这一点,但这一个是轻量级的,非常简单。虽然我第一次这么做时它闻起来很好笑,但我开始相信它闻起来比拥有一个全局
日志文件
变量要好。

我创建了这个helper decorator来轻松设置函数属性:

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



有趣。在python中实现静态变量还有其他方法吗?-1,这将用python中的生成器实现。这是一个很糟糕的理由来否定这个答案,因为它演示了C和python之间的类比,不提倡编写这个特定函数的最佳方法。@RobertRossney但是如果生成器是一种方法,那么这就是函数属性的糟糕使用。如果是这样,那么这就是滥用。不过,我不确定是否要对滥用投票权的行为进行纠正,因为问题也要求这些人:P@hobs我看不出这怎么会是对政治公众人物232的虐待。PEP 232为该机制提供了一些用例,但似乎并不建议仅限于这些用例。我们手头有类,为什么要推送函数?别忘了类可以模拟函数。同样
time.sleep(1)
优于
os.system('sleep 1')
@bgbg True,尽管这个例子不是关于睡眠的。这肯定是一种滥用;在这里使用函数是完全免费的。muhuk是完全正确的:类是一个更好的解决方案。我还想问,“与类相比,它的优势是什么?”以抵消许多Python程序员对此不太明显的缺点。+1这是滥用此功能的一个很好的例子,这也是问题的目的之一。这与mipadi的答案有什么不同?似乎是一样的,除了int之外,属性值是一个函数。
def test()
真的有必要吗?你认为这有缺点吗?e、 如果两个库尝试编写同一个临时属性会怎么样?我正想这么做。然后我停了下来。“这是个坏主意吗?”我想知道。然后,我就这样走了过去。经过一番周折,我找到了这个问题/答案。仍然不确定这是否是个好主意。这绝对是所有答案中函数属性的最合法使用(截至2012年11月)。大多数(如果不是全部)其他答案使用函数属性作为全局变量的替换;然而,它们并没有摆脱全局状态,这正是全局变量的问题所在。这是不同的,因为一旦设置了值,它就不会改变;它是恒定的。这样做的一个好结果是,您不会遇到全局变量固有的同步问题。是的,您可以提供自己的同步,但关键是:自动同步是不安全的。事实上,我说,只要属性不改变所讨论的函数的行为,它就是好的。与
。\uuuuu doc\uuuuuu
相比,这种方法还可以用于将输出描述附加到装饰函数,这在python 2中是不存在的。*。重新嗅到:这并不能摆脱全局日志文件。它只是将其存储在另一个全局日志函数中。@allyourcode:但是如果在同一个模块中必须为不同的函数创建一组全局日志文件,它可以帮助避免名称冲突。我同意你的主要观点,这很可能会让人困惑,但是重新记录字符串:是的,但是为什么函数有特殊属性呢?可能有一组固定的属性,其中一个用于保存docstring。@allyourcode在语言中设计了一般大小写,而不是特定的特殊大小写,这使事情变得更简单,并增加了与Python旧版本的兼容性。(例如,设置/操作docstring的代码仍然可以使用不做docstring的Python版本,只要它处理属性不存在的情况。)decorator如何帮助?为什么不呢
>>> def FakeObject():
...   def test():
...     print "foo"
...   FakeObject.test = test
...   return FakeObject
>>> x = FakeObject()
>>> x.test()
foo
def log(msg):
   log.logfile.write(msg)
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