每次调用函数时,函数属性do_something.n
都会增加。
让我感到困扰的是,我在该函数的外部声明了该属性do_something.n=0
。
我answered使用一个“函数属性” Using queue.PriorityQueue, not caring about comparisons来提供一个唯一的计数器,以供PriorityQueue使用-有一个nicer solution by MartijnPieters)
MCVE:
def do_something():
do_something.n += 1
return do_something.n
# need to declare do_something.n before usign it, else
# AttributeError: 'function' object has no attribute 'n'
# on first call of do_something() occures
do_something.n = 0
for _ in range(10):
print(do_something()) # prints 1 to 10
还有什么其他方法可以在函数“内部”定义函数的属性,从而避免忘记AttributeError: 'function' object has no attribute 'n'
了?
通过注释编辑了许多其他方式:
答案 0 :(得分:7)
内部并不多,但是装饰器使function属性更加明显:
def func_attr(**attrs):
def wrap(f):
f.__dict__.update(attrs)
return f
return wrap
@func_attr(n=0)
def do_something():
do_something.n += 1
return do_something.n
这可能比将属性初始化放在函数内的任何方法都更干净。
答案 1 :(得分:2)
使用内置的hasattr
函数怎么样?
def do_something():
if not hasattr(do_something, 'n'):
do_something.n = 1
do_something.n += 1
return do_something.n
作为参考,以下是hasattr
与try-except
的讨论:
hasattr() vs try-except block to deal with non-existent attributes
答案 2 :(得分:1)
这就是我提到您my answer另一个问题时所想到的:
def with_this_arg(func):
def wrapped(*args, **kwargs):
return func(wrapped, *args, **kwargs)
return wrapped
@with_this_arg
def do_something(this):
if not getattr(this, 'n', None):
this.n = 0
this.n += 1
return this.n
for _ in range(10):
print(do_something()) # prints 1 to 10
如果您喜欢更“ pythonic”的EAFP编码风格(会稍快一些),则可以这样编写:
@with_this_arg
def do_something(this):
try:
this.n += 1
except AttributeError: # First call.
this.n = 1
return this.n
这可以与@ user2357112的answer(如果以正确的顺序完成)结合在一起,而无需检查或异常处理:
def func_attr(**attrs):
def wrap(f):
f.__dict__.update(attrs)
return f
return wrap
def with_this_arg(func):
def wrapped(*args, **kwargs):
return func(wrapped, *args, **kwargs)
return wrapped
@func_attr(n=0)
@with_this_arg
def do_something(this):
this.n += 1
return this.n
for _ in range(10):
print(do_something()) # prints 1 to 10
答案 3 :(得分:1)
还有另外两种方式。第一种使用某些人称为“ functor”类的方法来创建具有所需属性的可调用对象,所有这些均来自该类的 。
这种方法不需要处理异常,因此唯一的运行时开销来自一次性创建其单个实例。
class do_something:
def __init__(self):
self.n = 0
def __call__(self, *args, **kwargs):
do_something.n += 1
return do_something.n
do_something = do_something() # Allow only one instance to be created.
for _ in range(10):
print(do_something()) # Prints 1 to 10.
第二种方法-非常“ pythonic”-将函数放在模块中(实际上是单例)。这就是我的意思:
文件do_something.py
:
n = 0
def do_something():
global n
n += 1
return n
示例用法(在其他脚本中):
from do_something import do_something
for _ in range(10):
print(do_something()) # Prints 1 to 10.
答案 4 :(得分:0)
遵循python Ask forgiveness not permission方法论,我唯一想到的方法是:
def do_something():
try:
do_something.n += 1
except AttributeError:
do_something.n = 1
return do_something.n
这将在第一次调用时自动生成属性,然后try:
代码块将起作用。
由于try:
... catch:
会产生一些开销,但这是我可以想到的解决此功能的唯一方法。