有没有一种方法可以将属性添加到函数中作为函数定义的一部分?

时间:2019-01-03 21:33:46

标签: python python-3.x

每次调用函数时,函数属性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'了?


通过注释编辑了许多其他方式:

5 个答案:

答案 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 

作为参考,以下是hasattrtry-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:会产生一些开销,但这是我可以想到的解决此功能的唯一方法。