【问题标题】:Python switch statement with aliases带有别名的 Python switch 语句
【发布时间】:2017-07-09 06:29:36
【问题描述】:

如何编写一个高效的“switch”语句,可以为不同的输入返回相同的内容?
Python中的简单开关可以使用这样的字典来实现:

def switch(s):
    case = {'phone': '123 456 789', 'website': 'www.example.com'}
    return case[s]

这个具有恒定的访问时间,但是我想使用别名,即switch('website') 将返回与switch('site') 等相同的东西,而不重复值,即不使用
case = {'website': 'www.example.com, 'site': 'www.example.com}
可以使用的是:

def switch(s):
    case = {('telephone', 'number', 'phone'): '123 456 789',
            ('website', 'site'): 'www.example.com'}
    for key, value in case.items():
        if s in key:
            return value

但这种方法比线性访问时间更差。
可以通过使用

使其保持不变
def switch(s):
    case = ['123 456 789', 'www.example.com']
    aliases = {'telephone': 0, 'number': 0, 'phone': 0,
               'website': 1, 'site': 1}
    return case[aliases[s]]

但是我有点重复值,如果我决定删除任何答案,我必须编辑aliases'和/或case的返回值(如果我不再想返回@ 987654329@ 我必须从case 中删除它并修改aliases 以便aliases['website']aliases['site'] 返回0 或在case 的第一个单元格中留下虚拟值或制作case 字典)

有没有更好的方法来编写这样的语句?

【问题讨论】:

  • 我不认为第二个例子有非线性时间。话虽如此,您的交换机池有多大?
  • 在第二个示例中,您线性遍历所有键,并在每个键中检查字符串是否在该键内,这也是无效的。
  • 怎么回事?让我们采取 2 个最坏和最好的情况:每个元组键的长度为 1。然后你得到 O(n);有一个元组键。 O(log(n))。
  • 要获得switch('site')(假设字典按照我上面写的方式排序),您必须在找到匹配项之前将'site'与所有5个值进行比较
  • 这是O(1)+O(1)=O(1)

标签: python data-structures conditional


【解决方案1】:

您可以使用链接哈希图方法:

def switch(s):
    alias = {'telephone': 1, 'number': 1, 'phone': 1,
             'website': 2, 'site': 2}
    case = {1: '123 456 789', 2: 'www.example.com'}
    return case[alias[s]]

这样您就可以保持O(1) 的查找时间。

当然,对于真实数据,您需要自动构建 aliascase 映射,但这应该相当简单。

更新/删除也应该相当简单,因为它们归结为简单的dict更新/删除。

此外,为了更轻松地插入新值,您可以使用 UUID4(或其他一些随机值)代替数字。

【讨论】:

  • 这正是我在第三个示例中使用的。
  • 不,您使用list 代替alias。使用dict 更新(删除)值不会有任何问题。
  • 就在第三个示例的正下方,我将其作为修改它的可能修复之一。 "或者把case 做成字典"
  • 我错过了那句话,抱歉。无论如何,我认为双 dict 查找是要走的路(因为我说的原因:不断查找、插入、更新和删除;而且开销很小)。
【解决方案2】:

除了原来的 case 字典外,我会简单地使用没有身份别名的 aliases 字典,并使用 get 检查潜在的别名:

def switch(s):
    case = {'phone': '123 456 789', 'website': 'www.example.com'}
    aliases = {'telephone': 'phone', 'number': 'phone', 'site': 'website'}
    return case[aliases.get(s, s)]  # check if it's an alias or use the input as-is

这样您就不需要复制值(不在casealias 中)。

【讨论】:

    【解决方案3】:

    在你的问题中你说:

    我想使用别名,即switch('website') 将返回与switch('site') 等相同的内容,而不会重复值

    我认为您对重复值的担忧是错误的,您不应该拒绝这种方法。添加具有相同字符串值的额外字典条目应该不是问题,这是解决问题的自然方法。如果不需要,请不要使用额外的间接层使代码复杂化。

    我假设您对这种方法的担忧是它可能会增加您的内存使用量,因为相同的值会在字典中存储多次。但大多数时候,您不会有多个单独的相同字符串,而是会有多个对同一个字符串对象的引用。由于字符串是不可变的,Python 可能会替换对预先存在的对象的引用,因为它应该创建另一个具有相同内容的独立字符串。

    您可以自己测试一下。尝试使用几个相同的字符串文字作为值创建一个字典,然后测试每个字典的 id

    d = {"a": "foo", "b": "foo", "c": "foo"}
    
    for val in d.values():
        print(id(val))
    

    在我的系统上,这告诉我ids 都是一样的。我认为同时编译的多个相同的字符串文字总是会变成对单个字符串对象的多个引用。在某些情况下,由于字符串“interning”,所有具有特定内容的字符串(通常看起来像是标识符的东西)将在程序的任何地方共享。但你可能不需要太在意细节。需要意识到的重要一点是,重复的字符串在大多数情况下可能不会使用过多的内存。

    我想不出任何其他理由反对将所有别名添加到单个字典中。这是自然的解决方案,所以我就这么做了。如果稍后内存使用成为问题,您可能会重新访问字典以仔细检查它是否填充了重复的引用,而不是重复的对象,但我怀疑它对任何严肃程序的规模都很重要。

    拥有易于使用和理解的代码更为重要。

    正如您所说,您的主要关注点不是重复自己,您可能希望使用代码设置字典来转换另一个冗余度稍低的数据结构,而不是直接将其作为文字进行。

    例如,以下代码使用字典推导将别名子列表与其值配对的列表转换为易于搜索的字典:

    _data = [     # contains (alias_list, value) 2-tuples
        (['telephone', 'number', 'phone'], '123 456 789'),
        (['website', 'site'], 'www.example.com'),
    ]
    
    case = {alias: value for aliases, value in _data for alias in aliases}
    

    您可能希望将此代码放在只运行一次的地方(例如,在顶层,或者在某个类或实例变量中),而不是每次调用 switch 函数时都运行字典推导.因为字典是可变的,Python 不会假设它可以为每次调用使用相同的 dict 对象(即使它总是具有相同的值)。

    【讨论】:

    • 我关心的不是运行时的内存使用,因为正如你所说,Python 很可能会优化它。我担心的是“如果我需要更改 1 个值会怎样?”。然后我必须更改每个键的值,但是因为它会保存在文件中,所以很容易错过我应该更改值的几个键。
    • 编写几行代码从包含别名列表及其值的序列(例如第二个示例中的数据类型)生成字典会很容易。我会将其添加到我的答案中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-08
    • 1970-01-01
    • 2019-06-12
    • 2018-09-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多