2012-07-02 66 views
5

有人能详细解释下面的代码的最后一行:Python的方法定义使用装饰

def myMethod(self): 
    # do something 

myMethod = transformMethod(myMethod) 

你为什么想要通过定义为通过其他方法的方法?那又如何呢?提前致谢!

回答

2

这是一个函数环绕的例子,当你有一个函数接受一个函数作为参数,并且返回一个新的函数来修改原始函数的行为。

下面是如何这可能是使用的例子,这是一个简单的包装它只是打印“确认”,并在每次调用“退出”:

def wrapper(func): 
    def wrapped(): 
     print 'Enter' 
     result = func() 
     print 'Exit' 
     return result 
    return wrapped 

,这里是你怎么可能一个例子使用此:

>>> def say_hello(): 
...  print 'Hello' 
... 
>>> say_hello() # behavior before wrapping 
Hello 
>>> say_hello = wrapper(say_hello) 
>>> say_hello() # behavior after wrapping 
Enter 
Hello 
Exit 

为方便起见,Python提供了decorator syntax这仅仅是函数封装的简写版本做同样的事情,在函数定义的时候,这里是如何可以用:

>>> @wrapper 
... def say_hello(): 
...  print 'Hello' 
... 
>>> say_hello() 
Enter 
Hello 
Exit 
+0

非常感谢您的帮助!我非常感谢它有多清楚。 –

2

为什么要通过另一种方法传递方法的定义?

因为你想修改它的行为。

那该怎么办?

完美的是,因为函数是Python中的第一类。

def decorator(f): 
    def wrapper(*args, **kwargs): 
    print 'Before!' 
    res = f(*args, **kwargs) 
    print 'After!' 
    return res 
    return wrapper 

def somemethod(): 
    print 'During...' 

somemethod = decorator(somemethod) 

somemethod() 
+0

为什么不修改方法的实际源代码?你能举一个例子,通过方法的定义会非常有益吗?最后,如何编写transformMethod来修改另一个方法?对于所有的问题抱歉,我从来没有见过这种类型的语法,所以我很困惑。 –

+2

“为什么不修改方法的实际源代码?”因为您可能没有*源代码,或者您需要将其应用于多个功能。 –

+1

@ user1495015或者,也许所做的修改总是相同的,只有实际修改的部分是不同的。为此,您可以愉快地使用装饰器。 – glglgl

1

你描述的是一个装饰,方法/函数修改的一种形式,可以实现与decorators特殊的语法容易得多。

你描述的是在@staticmethod形式,@classmethod@functools.wraps(),非常广泛地使用,例如相当于

@transformMethod 
def myMethod(self): 
    # do something 

装饰@contextlib.contextmanager等等,等等等等

由于有一定的Python版本(我认为它是2.6),课程也可以进行装饰。

这两种装饰愉快地允许返回甚至不是函数或类的对象。例如,你可以用一种将它变成字典,集合或其他的方式来装饰生成器函数。

apply = lambda tobeapplied: lambda f: tobeapplied(f()) 

@apply(dict) 
def mydict(): 
    yield 'key1', 'value1' 
    yield 'key2', 'value2' 
print mydict 

@apply(set) 
def myset(): 
    yield 1 
    yield 2 
    yield 1 
    yield 4 
    yield 2 
    yield 7 
print myset 

我在这里做什么?

我创建了一个函数,它接受一个“要应用的东西”,然后返回另一个函数。

这个“内部”函数需要对函数进行修饰,调用它并将其结果放入外部函数并返回此结果。

f()返回一个发生器对象,然后将其放入dict()set()

+0

不,它*是一个装饰器。对于完全相同的操作,“@”只是语法糖。 –

+0

@ IgnacioVazquez-Abrams你是对的。我对Python的定义太接近了。 – glglgl

+0

@ IgnacioVazquez-Abrams是的,但大多数人看到装饰相反。不一定作为一种通用的方法转换。 –

0

你需要明白,在Python中,一切都是一个对象。函数是一个对象。你可以用一个函数对象来做同样的事情,你可以用其他类型的对象来做:存储在列表中,存储在字典中,从函数调用中返回等等。

像你这样的代码的常见原因显示是“包装”其他功能对象。例如,这里是一个打印由函数返回的值的包装器。

def print_wrapper(fn): 
    def new_wrapped_fn(*args): 
     x = fn(*args) 
     print("return value of %s is: %s" % (fn.__name__, repr(x))) 
     return x 
    return new_wrapped_fn 

def test(a, b): 
    return a * b 

test = print_wrapper(test) 

test(2, 3) # prints: return value of test is: 6 

这是一个非常有用的任务,也是一个很普遍的任务,Python对它有特别的支持。 Google搜索“Python装饰器”。

0

在您的原始问题中,您问“为什么要通过其他方法传递方法的定义?”然后,在评论中,您问“为什么不修改方法的实际源代码?”实际上,我认为这是一个非常好的问题,如果不用手挥手就很难回答,因为装饰者只有在代码达到一定的复杂程度时才会变得非常有用。但是,我认为如果你考虑以下两个功能装饰的点将变得更加清晰:

def add_x_to_sequence(x, seq): 
    result = [] 
    for i in seq: 
     result.append(i + x) 
    return result 

def subtract_x_from_sequence(x, seq): 
    result = [] 
    for i in seq: 
     result.append(i - x) 
    return result 

现在,这两个例子功能有一定的缺陷 - 在现实生活中,例如,你可能只是重写他们作为列表解析 - 但让我们忽略目前的明显缺陷,并假装我们必须这样写,因为for循环遍历序列。我们现在面临的问题是,我们的两个功能差不多是一样的,只在一个关键时刻有所不同。这意味着我们在这里重复自己!这是一个问题。现在我们必须维护更多的代码行,为bug出现留出更多空间,并且在bug出现后隐藏更多空间。

一个经典方法这个问题可能是创建一个函数,需要的功能,和整个序列应用它,就像这样:

def my_map(func, x, seq): 
    result = [] 
    for i in seq: 
     result.append(func(i, x)) 
    return result 

现在我们需要做的就是定义特定funcs中传递给my_map(这实际上只是内置的map函数的专用版本)。

def sub(a, b): 
    return a - b 

def add(a, b): 
    return a + b 

而且我们可以使用它们像这样:

added = my_map(sub, x, seq) 

但这种方法有它的问题。例如,阅读比我们最初的独立功能要难一些;每次我们想从项目列表中加上或减去x,我们必须指定函数和值作为参数。如果我们这么做的话,我们宁愿有一个单一的函数名称,它总是指向同一个动作 - 这将提高可读性,并使我们更容易理解代码中发生的事情。我们可以包裹上面另一功能...

def add_x_to_sequence(x, seq): 
    return my_map(add, x, seq) 

但现在我们再次重复自己!而且我们也在创造一个功能繁多的混乱的名字空间。

装饰者提供了一个解决这些问题的方法。而不是每次将函数传递给另一个函数,我们可以传递一次。首先,我们定义了一个包装函数:

def vectorize(func): 
    def wrapper(x, seq): 
     result = [] 
     for i in seq: 
      result.append(func(i, x)) 
     return result 
    return wrapper 

现在我们需要做的就是定义一个函数,并把它传递给上面,它环绕:

def add_x_to_sequence(a, b): 
    return a + b 
add_x_to_sequence = vectorize(add_x_to_sequence) 

或者,使用修饰语法:

@vectorize 
def add_x_to_sequence(a, b): 
    return a + b 

现在我们可以写出许多不同vectorize d功能,并且我们for为所有这些的逻辑在短短一个发生PL高手。现在我们不必分别修复或优化许多不同的功能;我们所有的循环相关的错误和循环相关的优化发生在同一个地方;而且我们仍然可以获得特定功能的所有可读性优势。