跟踪Python中的函数调用+闭包(la SICP)数量 [英] Tracking number of function calls + closures (à la SICP) in Python

查看:81
本文介绍了跟踪Python中的函数调用+闭包(la SICP)数量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个有关 Python 中的范围闭包的问题,该问题是由SICP中的一个练习引起的。

This is a question about scope and closures in Python, motivated by an exercise in SICP. Much thanks for your time if you read this!

SICP中的一个问题(3.2)要求创建一个 make-monitored过程,该过程带有一个函数f。 (具有一个参数)作为输入,并返回一个跟踪f​​调用次数的过程。 (如果此新过程的输入为 num-calls,则返回调用f的次数;如果为 reset,则将counter重置为0,否则将f应用于输入并返回结果(在适当增加计数器后)。

A question (3.2) in SICP asks one to create a procedure "make-monitored", that takes in a function f (of one parameter) as input and returns a procedure that keeps track of how many times f has been called. (If the input to this new procedure is "num-calls" it returns the number of times f has been called, if it is "reset" it resets counter to 0 and anything else, it applies f to the input and returns the result (after appropriately incrementing the counter).

这是我编写的Scheme中有效的代码:

Here is code in Scheme that I wrote that works:

(define (make-monitored f)
  (let ((counter 0))
     (define (number-calls) counter)
     (define (reset-count)
       (set! counter 0))
     (define (call-f input)
       (begin (set! counter (+ 1 counter))
              (f input)))
    (define (dispatch message)
      (cond ((eq? message 'num-calls) (number-calls))
            ((eq? message 'reset) (reset-count))
            (else (call-f message))))
    dispatch))

但是我的问题是关于如何以 pythonic的方式编写此信息。我下面的尝试显然是直接翻译我的Scheme代码中,我意识到,尽管对于不纯函数语言(例如Scheme)来说,这是很好的选择,但它可能不是用Python做到这一点的最简洁的方法。如何在Python中解决这样一个普遍的问题,在Python中您希望一个高阶过程在类型上分派并记住本地状态?

My question however is about how to write this in a "pythonic" way. My attempt below is obviously a direct translation of my Scheme code and I realize that though it is fine for an impure functional language (like Scheme) it's probably not the cleanest or best way to do it in Python. How does one solve a general problem like this in Python where you want a higher order procedure to dispatch on type and remember local state?

下面是我的无效尝试(早些时候我没有说过,但问题是该程序的早期版本仍在终端的内存中)(在2中,似乎很难进行非局部变量绑定)

Below is my noobish attempt that works (earlier I had said it did not but the problem was that an earlier version of the program was still in the terminal's memory) (In 2 it seems hard to make nonlocal variable binding)

def make_monitored(func):
    counter = 0                      
    def dispatch(message):
        if message == "num-calls":
            return num_calls()
        elif message == "reset":
            reset()
        else:
            nonlocal counter
            counter += 1
            return func(message)
    def num_calls():
        nonlocal counter
        return counter
    def reset():
        nonlocal counter
        counter = 0
    return dispatch

PS:此q行为与中的同一套练习有关SICP ,但是我的问题确实是关于Python最佳实践的,而不是闭包或Scheme的概念...

PS: This question is related to this same set of exercises in SICP but my question is really about Python best practice and not the concept of closures or Scheme...

推荐答案

我认为编写一个将包装函数包装到类中的装饰器会更具有Pythonic性:

I think writing a decorator wrapping the function in a class would be more pythonic:

from functools import wraps 

def make_monitored(func):
    class wrapper:
        def __init__(self, f):
            self.func = f
            self.counter = 0
        def __call__(self, *args, **kwargs):
            self.counter += 1
            return self.func(*args, **kwargs)
    return wraps(func)(wrapper(func))

这样做的好处是它尽可能地模仿了原始功能,只是添加了一个计数器字段:

This has the advantage that it mimics the original function as close as possible, and just adds a counter field to it:

In [25]: msqrt = make_monitored(math.sqrt)
In [26]: msqrt(2)
Out[26]: 1.4142135623730951
In [29]: msqrt.counter
Out[29]: 1
In [30]: msqrt(235)
Out[30]: 15.329709716755891
In [31]: msqrt.counter
Out[31]: 2
In [32]: @make_monitored
    ...: def f(a):
    ...:     """Adding the answer"""
    ...:     return a + 42
In [33]: f(0)
Out[33]: 42
In [34]: f(1)
Out[34]: 43
In [35]: f.counter
Out[35]: 2
In [36]: f.__name__
Out[36]: 'f'
In [37]: f.__doc__
Out[37]: 'Adding the answer'

对于 f ,您还将看到用法作为装饰器,以及包装器如何保留原始名称和文档字符串(如果没有<$ c $,则不会这样) c> functools.wraps )。

For f, you also see the usage as a decorator, and how the wrapper keeps the original name and docstring (which would not be the case without functools.wraps).

定义重置作为练习读者,但很琐碎。

Defining reset is left as an exercise to the reader, but quite trivial.

这篇关于跟踪Python中的函数调用+闭包(la SICP)数量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆