有人能详细解释下面的代码的最后一行:Python的方法定义使用装饰
def myMethod(self):
# do something
myMethod = transformMethod(myMethod)
你为什么想要通过定义为通过其他方法的方法?那又如何呢?提前致谢!
有人能详细解释下面的代码的最后一行:Python的方法定义使用装饰
def myMethod(self):
# do something
myMethod = transformMethod(myMethod)
你为什么想要通过定义为通过其他方法的方法?那又如何呢?提前致谢!
这是一个函数环绕的例子,当你有一个函数接受一个函数作为参数,并且返回一个新的函数来修改原始函数的行为。
下面是如何这可能是使用的例子,这是一个简单的包装它只是打印“确认”,并在每次调用“退出”:
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
为什么要通过另一种方法传递方法的定义?
因为你想修改它的行为。
那该怎么办?
完美的是,因为函数是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()
为什么不修改方法的实际源代码?你能举一个例子,通过方法的定义会非常有益吗?最后,如何编写transformMethod来修改另一个方法?对于所有的问题抱歉,我从来没有见过这种类型的语法,所以我很困惑。 –
“为什么不修改方法的实际源代码?”因为您可能没有*源代码,或者您需要将其应用于多个功能。 –
@ user1495015或者,也许所做的修改总是相同的,只有实际修改的部分是不同的。为此,您可以愉快地使用装饰器。 – glglgl
你描述的是一个装饰,方法/函数修改的一种形式,可以实现与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()
。
不,它*是一个装饰器。对于完全相同的操作,“@”只是语法糖。 –
@ IgnacioVazquez-Abrams你是对的。我对Python的定义太接近了。 – glglgl
@ IgnacioVazquez-Abrams是的,但大多数人看到装饰相反。不一定作为一种通用的方法转换。 –
你需要明白,在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装饰器”。
在您的原始问题中,您问“为什么要通过其他方法传递方法的定义?”然后,在评论中,您问“为什么不修改方法的实际源代码?”实际上,我认为这是一个非常好的问题,如果不用手挥手就很难回答,因为装饰者只有在代码达到一定的复杂程度时才会变得非常有用。但是,我认为如果你考虑以下两个功能装饰的点将变得更加清晰:
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高手。现在我们不必分别修复或优化许多不同的功能;我们所有的循环相关的错误和循环相关的优化发生在同一个地方;而且我们仍然可以获得特定功能的所有可读性优势。
非常感谢您的帮助!我非常感谢它有多清楚。 –