问题:Python函数属性-使用和滥用
想改善这个问题吗? 更新问题,使其仅通过编辑此帖子 来关注一个问题。
4年前 关闭。
没有多少人知道此功能,但是Python的函数(和方法)可以具有attribute 。看哪:
>>> 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将语法规则与方法相关联。但是自定义属性呢?是否有充分的理由使用它们?
Closed . This question needs to be more
focused . It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post .
Closed 5 years ago .
Not many are aware of this feature, but Python’s functions (and methods) can have attributes . Behold:
>>> 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
What are the possible uses and abuses of this feature in Python ? One good use I’m aware of is PLY ‘s usage of the docstring to associate a syntax rule with a method. But what about custom attributes ? Are there good reasons to use them ?
回答 0
我通常使用函数属性作为注释的存储。假设我想以C#的方式编写(表示某种方法应该成为Web服务接口的一部分)
class Foo ( WebService ):
@webmethod
def bar ( self , arg1 , arg2 ):
...
然后我可以定义
def webmethod ( func ):
func . is_webmethod = True
return func
然后,当Web服务调用到达时,我查找该方法,检查基础函数是否具有is_webmethod属性(实际值无关紧要),如果该方法不存在或不打算通过Web调用,则拒绝该服务。
I typically use function attributes as storage for annotations. Suppose I want to write, in the style of C# (indicating that a certain method should be part of the web service interface)
class Foo(WebService):
@webmethod
def bar(self, arg1, arg2):
...
then I can define
def webmethod(func):
func.is_webmethod = True
return func
Then, when a webservice call arrives, I look up the method, check whether the underlying function has the is_webmethod attribute (the actual value is irrelevant), and refuse the service if the method is absent or not meant to be called over the web.
回答 1
我已经将它们用作函数的静态变量。例如,给出以下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
这肯定属于频谱的“滥用”端。
I’ve used them as static variables for a function. For example, given the following C code:
int fn(int i)
{
static f = 1;
f += i;
return f;
}
I can implement the function similarly in Python:
def fn(i):
fn.f += i
return fn.f
fn.f = 1
This would definitely fall into the “abuses” end of the spectrum.
回答 2
您可以用JavaScript的方式来做对象…虽然没有任何意义,但是它是可行的;)
>>> def FakeObject ():
... def test ():
... print "foo"
... FakeObject . test = test
... return FakeObject
>>> x = FakeObject ()
>>> x . test ()
foo
You can do objects the JavaScript way… It makes no sense but it works ;)
>>> def FakeObject():
... def test():
... print "foo"
... FakeObject.test = test
... return FakeObject
>>> x = FakeObject()
>>> x.test()
foo
回答 3
我很少使用它们,但是它们可以很方便:
def log ( msg ):
log . logfile . write ( msg )
现在,我可以log
在整个模块中使用,只需设置即可重定向输出log.logfile
。有很多其他方法可以实现这一点,但是这种方法轻巧而简单。虽然我第一次这样做时闻起来很可笑,但我开始相信它闻起来比具有全局logfile
变量更好。
I use them sparingly, but they can be pretty convenient:
def log(msg):
log.logfile.write(msg)
Now I can use log
throughout my module, and redirect output simply by setting log.logfile
. There are lots and lots of other ways to accomplish that, but this one’s lightweight and dirt simple. And while it smelled funny the first time I did it, I’ve come to believe that it smells better than having a global logfile
variable.
回答 4
函数属性可用于编写将代码和关联数据包装在一起的轻量级闭包:
#!/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
Function attributes can be used to write light-weight closures that wrap code and associated data together:
#!/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
回答 5
我创建了这个辅助装饰器来轻松设置函数属性:
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
I’ve created this helper decorator to easily set function attributes:
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
A use case is to create a collection of factories and query the data type they can create at a function meta level.
For example (very dumb one):
@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
回答 6
有时,我使用函数的属性来缓存已计算的值。您也可以使用通用装饰器来概括此方法。注意此类功能的并发问题和副作用!
Sometimes I use an attribute of a function for caching already computed values. You can also have a generic decorator that generalizes this approach. Be aware of concurrency issues and side effects of such functions!
回答 7
我一直以为,这是唯一可能的原因,因此在逻辑上可以放置文档字符串或其他类似的东西。我知道如果我将它用于任何生产代码,都会使大多数阅读它的人感到困惑。
I was always of the assumption that the only reason this was possible was so there was a logical place to put a doc-string or other such stuff. I know if I used it for any production code it’d confuse most who read it.