【问题标题】:AttributeError issues with the decorator装饰器的 AttributeError 问题
【发布时间】:2021-07-07 03:00:28
【问题描述】:

我一直在玩反转单链表,一切正常,但后来我决定添加一个装饰器来计时函数的执行。现在,当我尝试运行代码时,会出现这样的错误:

Traceback (most recent call last):
  File "C:/Users/cherp2/AppData/Roaming/JetBrains/PyCharmCE2020.3/scratches/scratch_7.py", line 86, in <module>
    main()
  File "C:/Users/cherp2/AppData/Roaming/JetBrains/PyCharmCE2020.3/scratches/scratch_7.py", line 78, in main
    print_ll(ll)
  File "C:/Users/cherp2/AppData/Roaming/JetBrains/PyCharmCE2020.3/scratches/scratch_7.py", line 10, in wrapped
    f(*args, **kwargs)
  File "C:/Users/cherp2/AppData/Roaming/JetBrains/PyCharmCE2020.3/scratches/scratch_7.py", line 37, in print_ll
    vals.append(str(node.val))
AttributeError: 'NoneType' object has no attribute 'val'

我的装饰器代码如下。我查了一下,和here的答案差不多:

def func_time(f):
    def wrapped(*args, **kwargs):
        t = dt.datetime.now()
        f(*args, **kwargs)
        print(f'{f.__name__} -- elapsed: {dt.datetime.now() - t}')
    return wrapped

问题是否会因为我在大多数函数中使用递归而出现?这是链表节点定义和错误函数之一:

from dataclasses import dataclass, field
import datetime as dt
from typing import Optional
import random


@dataclass(order=True)
class Node:
    val: int
    next: 'Node' = field()


def make_ll(n, head: Optional[Node] = None, node: Optional[Node] = None):
    if not head:
        head = Node(random.randint(0, 20), None)
        node = head
    node.next = Node(random.randint(0, 20), None)
    if n == 0:
        return head
    else:
        return make_ll(n-1, head, node.next)


@func_time
def print_ll(node: Node, vals: Optional[list] = None):
    if not vals:
        vals = []
    vals.append(str(node.val))
    if node.next:
        return print_ll(node.next, vals)
    else:
        vals.append('None')
        print(' -> '.join(vals))


def main():
    ll = make_ll(10, None)
    print('Original list: ')
    print_ll(ll)


if __name__ == '__main__':
   main()

编辑: 看起来这绝对是递归的问题。我在非递归函数上测试了装饰器,它按预期工作:

@func_time
def add(l):
    running_sum = 0
    for a in l:
        running_sum += a
    return running_sum

>>> l = [i for i in range(10**6)]
>>> x = add(l)
Out: add -- elapsed: 0:00:00.052296

EDIT2: 测试问题中的代码:

def main():
    # ll = make_ll(10, None)
    ll = Node(1, None)
    ll.next = Node(2, None)
    ll.next.next = Node(3, None)
    ll.next.next.next = Node(4, None)
    ll.next.next.next.next = Node(5, None)
    print('Original list: ')
    print_ll(ll)

    rev = reverse_ll(ll)
    print('Reversed list: ')
    print_ll(rev)

>>> main()

Out: 
Original list: 
1 -> 2 -> 3 -> 4 -> 5 -> None

Reversed list: 
5 -> 4 -> 3 -> 2 -> 1 -> None

【问题讨论】:

  • 我认为问题在于你的装饰器 isn't 写得正确(不是递归)。但是我无法测试该理论,因为您没有提供minimal reproducible example。请编辑您的问题并添加一个。
  • 我已在问题中附加了代码以使其成为 MRE。我还在底部的 EDIT 部分添加了一个示例,说明装饰器可以很好地处理非递归函数。
  • 您添加的代码不会产生问题中显示的AttributeError - 尽管它没有做非递归所做的事情(也不是您想要的)。
  • 您评论的第二部分非常神秘。你是什​​么意思:它不做非递归所做的(也不是你想要的)
  • 哪部分不清楚?您问题中现在版本中的代码不会导致错误,但无法正常工作并且也无法执行您想要的操作。 (有一个不同的修复方法——装饰器问题不会影响它。)

标签: python recursion decorator


【解决方案1】:

好的,这很尴尬,但发生的事情是我忘记向装饰器添加 return 语句,所以当它被递归使用时,在第一次递归时它返回 None 然后递归失败,因为 None不是可接受的输入参数。

简单地说,这解决了所有问题:

def func_time(f):
    def wrapped(*args, **kwargs):
        t = dt.datetime.now()
        foo = f(*args, **kwargs)
        print(f'{f.__name__} -- elapsed: {dt.datetime.now() - t}')
        return foo
    return wrapped

【讨论】:

  • 是的,这是我在根据您的问题发表初步评论时注意到的装饰器问题,并将摆脱AttributeError。然而,装饰器将分别对 print_ll() 函数的每次递归调用进行计时——我怀疑这是你想要发生的。 这就是我的“神秘”言论的全部内容。
  • 哦,是的,但不,这实际上是我想要的 - 测量每个递归步骤的执行。
猜你喜欢
  • 2016-03-23
  • 1970-01-01
  • 2016-10-05
  • 2011-05-18
  • 2019-05-11
  • 2015-05-12
  • 2011-07-09
  • 2022-01-18
相关资源
最近更新 更多