Discuss / Python / 请问廖老师,像这样wrapper函数没有return的情况下,是否也算是装饰器呢?

请问廖老师,像这样wrapper函数没有return的情况下,是否也算是装饰器呢?

Topic source

萌13131313

#1 Created at ... [Delete] [Delete and Lock User]

请问廖老师,像这样wrapper函数没有return的情况下,是否也算是装饰器呢?

def callbe(func):
    def wrapper(*args,**kw):
        print('begin call: %s' % func.__name__)
        func(*args,**kw)
        print ('end call: %s' % func.__name__)
    return wrapper

@callbe
def log():
    print('Hello,world!')

log()
begin call: log
Hello,world!
end call: log

廖雪峰

#2 Created at ... [Delete] [Delete and Lock User]

装饰器不能改变原函数的行为

VV谢小鑫

#3 Created at ... [Delete] [Delete and Lock User]

我觉得可以,因为now()函数本身没有返回值, 所以wrapper()没有返回值,符合“装饰器不改变原函数的行为”

我理解这样不算装饰器,因为这样你的log函数就改变了,与wrapper函数相同。不知这样理解是否正确,请指教

灰_手

#5 Created at ... [Delete] [Delete and Lock User]

@VV谢小鑫 那函数装饰任何有返回值的函数结果都会变得None,改变原函数行为的都不能算是装饰器。

def callbe(func):
    def wrapper(*args,**kw):
        print('begin call: %s' % func.__name__)
        func(*args,**kw)
        print ('end call: %s' % func.__name__)
    return wrapper

@callbe
def add_one(num):
    retrun num + 1


print(add_one(2))

# begin call: add_one
# None
# end call: add_one

LINTINGJIE丶

#6 Created at ... [Delete] [Delete and Lock User]

@灰_手 为什么会返回None呢,求指导

灰_手

#7 Created at ... [Delete] [Delete and Lock User]

就是因为func(*args,**kw)的值没有被正确返回,所以任何被@callbe装饰过的函数最终结果都是None.

ksven

#8 Created at ... [Delete] [Delete and Lock User]

@灰手 的解释是对的,但是不太容易懂,我来解释一下。 首先@灰手 的输出是错的,不知道是不是有意的。我用的是pycharm 2016.1.4,JRE1.8.0,Python3.5.2,输出是:

begin call: add_one
end call: add_one
None

假定你已经深刻理解了廖老师说的: “decorator就是一个返回函数的高阶函数” 所以@灰_手 的程序等价于:

...
def add_one(num):            ①
    retrun num + 1            ②
add_one = callbe(add_one)    ③
...

而③又等价于add_one = wrapper。那么接下来就明显了: print(add_one(2)) 先调用了add_one即wrapper,而wrapper里现在是什么呢

print('begin call: %s' % func.__name__)
func(*args, **kw)  # 以传入的参数调用func函数,所以这句等价于3
print('end call: %s' % func.__name__)

即wrapper里等价于

print('begin call: %s' % func.__name__)
3
print('end call: %s' % func.__name__)

这个‘3’没有对象接收!!! 即@灰_手 说的:“值没有被正确返回”,所以不会被打印!!!所以那个return是必要的!!! 至于第三行的输出为什么是None。因为现实调用add_one即调用wrapper会输出前两行,然后print(add_one(2))会输出add_one这个变量本身!(因为上面所说的那个‘3’没有被add_one接收!!)而add_one现在指向wrapper,但是wrapper函数不在这句print的作用域之内!!所以最后一行输出为None。 啊好绕啊。

灰_手

#9 Created at ... [Delete] [Delete and Lock User]

LS基本正解。我懒得运行,导致输出顺序有误。但有一个地方需要更正的是:任何函数都有返回值,如果没有显式return的话,在函数最后会自动加一句return None的。wrapper函数就是因为没有显式返回,所以它的返回值是None,于是任何一个被包装的函数最终返回值都是None

萌13131313

#10 Created at ... [Delete] [Delete and Lock User]

明白了。

原代码:

def callbe(func):
    def wrapper(*args,**kw):
        print('begin call: %s' % func.__name__)
        func(*args,**kw)
        print ('end call: %s' % func.__name__)
    return wrapper

@callbe
def log():
    print('Hello,world!')

下面是我的分析,供大家参考:

log在运行时实际上是这样的:

log = callbe(log)

因为callbe的返回值是wrapper,所以:

log = wrapper

当调用log时,实际上调用的是wapper:

log() == wrapper()

wapper函数体如下:

print('begin call: %s' % func.__name__)
func(*args,**kw)
print ('end call: %s' % func.__name__)

基于闭包机制,wrapper会自动记住上层函数赋值给func的log函数对象:

callbe(log)  # func = log

func函数体如下:

print('Hello,world!')

可以看到func的函数体没有return,所以默认返回None,可以手动测试:

>>> def test():
...     print('Hello,world!')
...
>>> x = test()
Hello,world!
>>> print(x)
None

再来看wrapper,wrapper函数体也没有return:

def callbe(func):
    def wrapper(*args,**kw):
        print('begin call: %s' % func.__name__)
        func(*args,**kw)
        print ('end call: %s' % func.__name__)
    return wrapper

所以wrapper也返回了None:

>>> print(log())
begin call: log
Hello,world!
end call: log
None

装饰器的定义是:“为函数动态添加一些功能,并不能改变原函数的行为”。将这个定义应用到本例中可以发行:wrapper返回了自己的None,而非func的None。也就是说wrapper改变了func的返回值...

为了更能说明问题,增加一个返回值为2的装饰函数:

@callbe
def test():
    return 2

由装饰器的定义可以知道,wrapper应该返回函数的值,也就是2。

现在打印该装饰函数的调用结果:

>>> print(test())
begin call: test
end call: test
None

wrapper返回了None却不是2。

正确的写法如下:

def callbe(func):
    def wrapper(*args,**kw):
        print('begin call: %s' % func.__name__)
        result = func(*args,**kw)
        print ('end call: %s' % func.__name__)
        return result
    return wrapper

@callbe
def test():
    return 2

调用后,可以看到如下结果:

>>> print(test())
begin call: test
end call: test
2

wrapper返回了test的返回值2。


  • 1
  • 2

Reply