Python 函数定义中的点符号

dot notation in Python's function definition

我知道Python支持面向对象的结构,它使用点符号。 但是,我对下面的代码感到困惑,其中点符号出现在没有定义 class 的函数定义中。 [我猜] Python 中是否将某些功能定义为函数属性?

def count(f):
    def counted(*args):
        counted.call_count += 1
            return f(*args)
        counted.call_count = 0
        return counted

第二个问题:是否有替代方案可以使用非本地语句而不是点符号来重写上面的代码来记录call_count?

闭包比函数属性更健壮。可以想象,您可以将 counted 绑定到其他东西,然后 counted.call_count 将不再是您想要的属性。

def count(f):
    call_count = 0
    def counted(*args):
        nonlocal call_count
        call_count += 1
        return f(*args)
    return counted

每次调用 count 时,都会创建一个 new 变量 call_count。在 count returns 之后,对该变量的唯一引用是在 counted 的主体内,而不是(很容易)对引用 counted 的任何内容可见。 =18=]

如果您作为调用者不需要直接访问 call_count(因为,比方说,装饰器仅在内部使用它),@chepner 展示的方法就是要走的路。

它的问题是,call_count 无法从外部访问。部分选项:

  1. 保留为函数的属性。

    • 但这不是一个好主意,因为到处都共享一个函数对象。如果你想在两个不同的地方独立地计算同一个函数的调用,你就会遇到问题。
  2. Pass 是一个可变容器,会被装饰器改变:

    def count(count_container):
        count_container[0] = 0
    
        def inner(f):
            def counted(*args):
                count_container[0] += 1
                return f(*args)
            return counted
        return inner
    
    
    l = [0]
    
    
    @count(l)
    def f():
        print("Test")
    
    >>> l
    [0]
    >>> f()
    Test
    >>> f()
    Test
    >>> l
    [2]
    

    当然,这样做的缺点是,呃。即使您将列表替换为自定义的专用对象 dataclass,这仍然很糟糕。

  3. 放弃使用@符号作为装饰器的想法。这为您提供了更多选择。

    • 你可以保留你现在的代码,手动传入函数:

       def count(f):
           def counted(*args):
               counted.call_count += 1
               return f(*args)
           counted.call_count = 0
           return counted
      
       >>> counted = count(f)
       >>> counted()
       Test
       >>> counted()
       Test
       >>> counted.call_count
       2
      

      这好多了,因为每次显式调用 count return 都是一个 new 函数,而不是给单个全局函数赋予这样的属性前。如果您想同时跟踪两个单独的调用实例,您只需调用 count 两次,然后保留每个 returned 函数。

    • 你也可以return一个getter。使用@chepner 代码的修改版本:

      def count(f):
          call_count = 0
          def counted(*args):
              nonlocal call_count
              call_count += 1
              return f(*args)
          return counted, lambda: call_count
      
      >>> counted, counter_getter = count(f)
      >>> counted()
      Test
      >>> counted()
      Test
      >>> counter_getter()
      2
      

      这里的主要好处是它让调用者可以在他们想要阅读时访问 call_count但是 不给他们修改它的能力。


抱歉,如果我在某处漏掉了一些缩进。 令人难以置信 尝试在嵌套点列表中格式化代码是令人沮丧的。