Python函数定义中的点表示法

Posted

技术标签:

【中文标题】Python函数定义中的点表示法【英文标题】:dot notation in Python's function definition 【发布时间】:2021-03-04 20:57:51 【问题描述】:

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

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

第二个问题:上面的代码是否可以改写为使用nonlocal语句而不是点符号来记录call_count?

【问题讨论】:

是的。函数也是可以具有属性的对象 函数属性被添加到PEP 232 并且功能与任何类相同。要使用非本地,只需创建一个call_count 变量并将nonlocal call_count 放在counted 函数的开头即可。 酷,这很有帮助,谢谢你们! 【参考方案1】:

如果您作为调用者不需要直接访问call_count(例如,它仅由装饰器在内部使用),那么@chepner 显示的将是可行的方法。

问题在于,call_count 无法从外部访问。一些选项:

    把它作为函数的一个属性。

    但这不是一个好主意,因为只有一个函数对象在各处共享。如果你想在两个不同的地方独立计算同一个函数的调用,你会遇到问题。

    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]
    

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

    放弃使用@ 表示法将其用作装饰器的想法。这为您提供了更多选择。

    您可以保留当前代码,并手动传入函数:

     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 的每次显式调用都会返回一个new 函数,而不是像以前那样为单个全局函数赋予属性。如果您想同时跟踪两个单独的调用实例,您只需调用两次count,然后保留每个返回的函数。

    你也可以返回一个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但是它不给他们修改它的能力。


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

【讨论】:

我在您的第 3 项中尝试了两种不同的方法,它们都有效。具有两个函数的元组作为返回的非本地函数看起来很有趣,它解决了我的问题。阅读您的代码我没有任何问题。非常感谢!【参考方案2】:

闭包比函数属性更健壮。可以想象,您可以将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,都会创建一个变量call_count。在count 返回后,对这个变量的唯一引用是在counted 的主体内,而不是(很容易)对任何引用counted 的东西可见。

【讨论】:

我在调用 count(f) 后无法获取 call_count 信息。请问执行后如何获取call_count?

以上是关于Python函数定义中的点表示法的主要内容,如果未能解决你的问题,请参考以下文章

RIDE如何调用自定义Python文件中的函数

Python定义函数加入箭头->

在一个python脚本中调用另一个python脚本中的函数

python学习手册中的一些易忘的点(4-7部分)

python中的lambda函数

如何解决Python中的函数逼近任务?