【问题标题】:Hard coded variables in python functionpython函数中的硬编码变量
【发布时间】:2015-11-17 17:17:18
【问题描述】:

有时,某些值/字符串在函数中是硬编码的。例如在以下函数中,我定义了一个“常量”比较字符串并对其进行检查。

def foo(s):
    c_string = "hello"
    if s == c_string:
        return True
    return False

没有过多讨论为什么这样做不好,以及应该如何在外部范围内定义它,我想知道当它以这种方式定义时,幕后会发生什么。 每次调用都会创建字符串吗?
如果不是字符串"hello",而是列表:[1,2,3](或者如果重要的话,是一个包含可变内容的列表)会发生同样的情况吗?

【问题讨论】:

    标签: python constants python-internals


    【解决方案1】:

    因为字符串是不可变的(就像元组一样),它与函数的字节码对象一起存储。它是通过一个非常简单和快速的索引查找来加载的。这实际上比全局查找

    您可以在字节码的反汇编中看到这一点,使用dis.dis() function

    >>> import dis
    >>> def foo(s):
    ...     c_string = "hello"
    ...     if s == c_string:
    ...         return True
    ...     return False
    ... 
    >>> dis.dis(foo)
      2           0 LOAD_CONST               1 ('hello')
                  3 STORE_FAST               1 (c_string)
    
      3           6 LOAD_FAST                0 (s)
                  9 LOAD_FAST                1 (c_string)
                 12 COMPARE_OP               2 (==)
                 15 POP_JUMP_IF_FALSE       22
    
      4          18 LOAD_GLOBAL              0 (True)
                 21 RETURN_VALUE        
    
      5     >>   22 LOAD_GLOBAL              1 (False)
                 25 RETURN_VALUE        
    >>> foo.__code__.co_consts
    (None, 'hello')
    

    LOAD_CONST 操作码从co_costs 数组中加载字符串对象,该数组是函数代码对象的一部分;引用被推到栈顶。 STORE_FAST 操作码从栈顶获取引用并将其存储在 locals 数组中,这又是一个非常简单且快速的操作。

    对于可变文字({..}[..]),特殊操作码构建对象,其内容仍尽可能视为常量(更复杂的结构遵循相同的构建块):

    >>> def bar(): return ['spam', 'eggs']
    ... 
    >>> dis.dis(bar)
      1           0 LOAD_CONST               1 ('spam')
                  3 LOAD_CONST               2 ('eggs')
                  6 BUILD_LIST               2
                  9 RETURN_VALUE        
    

    BUILD_LIST 调用使用两个常量字符串对象创建新的列表对象。

    有趣的事实:如果您使用列表对象进行成员资格测试(something in ['option1', 'option2', 'option3'] Python 知道列表对象永远不会发生变异,并且会在编译时将其转换为元组(所谓的窥孔优化)。这同样适用于集合文字,它被转换为 frozenset() 对象,但仅在 Python 3.2 和更新版本中。请参阅 Tuple or list when using 'in' in an 'if' clause?

    请注意,您的示例函数使用布尔值相当冗长;你本来可以用的:

    def foo(s):
        c_string = "hello"
        return s == c_string
    

    为了获得完全相同的结果,避免在 Python 2 中调用 LOAD_GLOBAL(Python 3 制作了 TrueFalse 关键字,因此这些值也可以存储为常量)。

    【讨论】:

    • s == "hello" 不会发生同样的事情吗?
    • 你对元组的评论是对的,我应该问一下列表。我会纠正这个问题。剩下的答案现在很好:)
    • @ArthurVaiselbuh:我现在也涵盖了列表,以及{...} 字典和集合。
    猜你喜欢
    • 2021-08-25
    • 2019-09-02
    • 2019-08-12
    • 2022-08-13
    • 1970-01-01
    • 1970-01-01
    • 2014-03-27
    • 1970-01-01
    • 2017-08-17
    相关资源
    最近更新 更多