Python中的类型推导[重复]

Posted

技术标签:

【中文标题】Python中的类型推导[重复]【英文标题】:Type deduction in Python [duplicate] 【发布时间】:2018-03-14 06:16:38 【问题描述】:

在 Python 中有没有更好的方法来确定函数参数是单个数字还是数字列表。目前我正在对通道控制流使用异常的副作用,但它看起来并不优雅:

def buildPattern( NumberOrList, Framework ):
    FinalPattern = '-->'
    try:
        ListLen = len(NumberOrList)
        #if this is a list, program flow will reach this point
        I = 0
        for Char in Framework:
            if Char == 'x':
                FinalPattern = FinalPattern + ' ' + str(NumberOrList[I])
                I = (I+1) % ListLen
            else:
                FinalPattern = FinalPattern + '  '
    except:
        #if we reach this point, we don't have a list... (or some other problem)
        for Char in Framework:
            if Char == 'x':
                FinalPattern = FinalPattern + ' ' + str(NumberOrList)
            else:
                FinalPattern = FinalPattern + '  '
    return FinalPattern

print buildPattern( 3,'x x x x x x x x ' ) #single number call
print buildPattern( [1,3,5], 'x x x x x x x x ' ) #list call

【问题讨论】:

如果它是一个单一的 int 就把它放在一个列表中:if type(NumberOrList) == 'int': NumberOrList = [NumberOrList] @jordanm 你写的不是pythonic,甚至不会像你写的那样工作。 【参考方案1】:

可以使用异常处理来执行此操作,但正如您所说,它不是很优雅。此外,如果经常引发异常,效率也不高,使用显式测试会更快。

实际上,重新设计代码会更加优雅。 ;) 仅仅因为 Python 允许您定义一个函数,其中 arg 可以是数字或列表,但这并不意味着它是一个好主意。正如您所发现的那样,它往往会使函数的内部逻辑更加复杂,并且经常导致在不同的执行路径中出现重复的代码。不过不管怎样……

测试 arg 的简洁方法是查看它是否是可迭代的,您可以使用 collections.Iterable 类来执行此操作。在 Python 的现代版本中,该类已移至 collections.abc 模块,但目前仍可在 collections 中使用,以便更轻松地编写在 Python 2 和 Python 3 上正确运行的代码。

顺便说一句,使用“裸”except 通常不是一个好主意。您应该始终命名您想要捕获的异常,否则您最终可能会捕获到您不期望的东西,并且您的代码将无法正确处理。

另外,关注PEP-0008 style guide 也是一个好主意。它使其他人更容易阅读您的代码。

这是您的函数的更紧凑版本,使用 PEP-0008 样式名称编写。它使用itertools.cycle 来简化源数据的循环。它还将输出字符串收集到一个列表中,并在一个步骤中将它们连接在一起。这比在循环中进行字符串连接更有效。此代码在 Python 2 和 Python 3 上都能正常运行。

from __future__ import print_function
from collections import Iterable
from itertools import cycle

def build_pattern(source, framework):
    if not isinstance(source, Iterable):
        source = [source]
    source = cycle(map(str, source))
    final_pattern = ['-->']
    for char in framework:
        final_pattern.append(next(source) if char == 'x' else ' ')
    return ' '.join(final_pattern)

print(build_pattern(3, 'x x x x x x x x ')) #single number call
print(build_pattern([1, 3, 5], 'x x x x x x x x ')) #list call
print(build_pattern('abcde', 'x x x x x x x x ')) #string call

输出

--> 3   3   3   3   3   3   3   3  
--> 1   3   5   1   3   5   1   3  
--> a   b   c   d   e   a   b   c  

正如 VPfB 在 cmets 中提到的,字符串是可迭代的,因此如果您将字符串传递给我的 build_pattern,它将直接传递给 cycle(map(str, source)),它不会被包装在列表中。这在这里很好,但是在某些情况下这种行为可能会导致问题,典型的情况是任意嵌套列表的展平。答案here 显示了如何处理这种情况。

【讨论】:

每个使用isinstance(arg, Iterable) 的人都应该注意一个问题。它适用于数字和许多其他类型,但不适用于字符串。单个字符串是可迭代的!这是为了支持“不是一个好主意”的观点。 @VPfB 好点,我想我应该在回答中提到这一点。但是,在这种特殊情况下,这并不重要,因为如果我们将字符串传递给 build_pattern ,它仍然会做正确的事情,正如我的代码中的最后一行所展示的那样。【参考方案2】:

你为什么不试试这个

def buildPattern(NumberOrList, Framework):
    if isinstance(NumberOrList, list):
       # This is list
    else:
       # This is number

这是 pep-8 python 风格指南推荐的,而不是 type(obj)

参考

https://www.python.org/dev/peps/pep-0008/#programming-recommendations

【讨论】:

【参考方案3】:

只需在函数顶部使用带有 isinstance 的 if 语句:

if isinstance(NumberOrList, int):
    # Its a number
elif isinstance(NumberOrList,list):
    # Its a list
else:
    # Its something you dont want

【讨论】:

以上是关于Python中的类型推导[重复]的主要内容,如果未能解决你的问题,请参考以下文章

好好学python·集合

好好学python·集合

python中的生成器表达式,各种推导式及内置函数

好好学python · 字典

python基础列表推导式&字典推导式&集合推导式

好好学python · 你真的会列表吗(列表推导式质检员)