测试装饰器的返回值时出错

时间:2019-04-22 20:34:18

标签: python

我正在尝试测试对包装函数的理解,因此构建了这段代码

def wrapper():
    print ("Wrapper")
    def wrapped():
        print ("Wrapped")
    return wrapped 

if int (wrapper()):
    print ("Returned integer")

我尝试了if str(wrapper())if callable (wrapper()),没关系

我不明白为什么if int (wrapper())会产生错误:

Exception has occurred: TypeError
int() argument must be a string, a bytes-like object or a number, not 'function'

当然应该只说False,根本没有错误吗?

3 个答案:

答案 0 :(得分:2)

我不知道您正在测试什么,但是我可以尝试解释您的代码做什么。

案例1:if str(wrapper())

  1. 调用wrapper()时,它会打印“包装器”,然后返回一个名为wrapped的函数。

  2. 因此,调用str(wrapper())等效于str(wrapped)。请注意,wrapped之后没有括号。这意味着str函数的参数是函数对象

    功能对象上调用str时,它将返回带有函数名称和位置的字符串。检查以下示例:

>>> def test():
...     pass
... 
>>> str(test)
'<function test at 0x7fa95c504668>'
>>> type(str(test))
<type 'str'>
  1. 因此,if str(wrapper())的评估结果为if '<function wrapped at 0x7fa95c504668>'
  2. 对于所有非空字符串,都会执行if语句。看这个例子:
>>> if "string":
...     print("Non empty string")
... 
Non empty string
>>> if "":
...     print("This line will not be printed")
... 
>>> 

案例2:if callable(wrapper())

  1. 与以前相同

  2. callable检查对象是否具有__call__属性

  3. 调用callable(wrapper())等效于callable(wrapped)。由于wrapped是一个函数,因此它具有__call__属性,并且表达式的计算结果为True

  4. 因此if callable(wrapper())的计算结果为if True,如果执行if语句!

案例3:if int(wrapper())

  1. 与以前相同

  2. int只能 将数字和字符串转换为整数。

  3. int(wrapper())等效于int(wrapped)。因此,您尝试使用函数对象

  4. 来调用int
  5. int无法将功能对象转换为整数并给出箭头消息:

Exception has occurred: TypeError
int() argument must be a string, a bytes-like object or a number, not 'function'

答案 1 :(得分:1)

Decorator只是一个函数的奇特名称,该函数将一个函数作为参数并返回一个函数。因此,装饰器的一个示例是:

def wrapper(func):
    print ("Wrapper")
    def wrapped(*args, **kwargs):
        print ("Wrapped")
        func(*args, **kwargs)
    return wrapped 

@wrapper
def to_wrap():
    print("decorated function")

如果您运行to_wrap(),则实际上您正在运行wrapped()等于func的{​​{1}}。

因此,请记住这个想法,让我们来看一下您的代码:

to_wrap

if int (wrapper()): print ("Returned integer") 返回的是一个函数(在您的代码或上面的修改中),然后您尝试将其转换为wrapper()并检查其布尔值。此转换未实现,因此Python引发TypeError,因为函数类型的对象无法转换为整数类型。

答案 2 :(得分:1)

仍然需要调用装饰器。您现在拥有的是 closure ,它返回一个函数:

def f():
    def wrapped():
         return 10
    return wrapped

x = f()
# x is the function 'wrapped'

请注意,它没有调用wrapped,只是返回了它。您将需要致电x来更改此行为:

x()
10

# Which is implicitly calling wrapped() to return 10

装饰器语法会为您解决此问题:

def f(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper # still returns an un-called function

@f
def x():
    return 10

x()
# 10

@f将有效执行f(x())的地方。因为您没有调用该函数,所以您永远无法获得返回的类型,因此int会引发一个TypeError

在您的示例中明确地:

def wrapper():
    print("wrapper")
    def wrapped():
        print("wrapped")
        return "1" # you need to return a value here, otherwise it will return None
    return wrapped

int(wrapper()())
# wrapper
# wrapped
# 1

请注意,在调用wrapper之后,多余的括号被称为wrapped。另外,如果您没有从wrapped返回值,那么您将返回None,这将引发一个不同的TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'