繁体   English   中英

在单个项目上“迭代”的pythonic方式是什么?

[英]What is the pythonic way of "iterating" over a single item?

我经常遇到这个问题,如果没有一些非常简单和 Pythonic 的单线解决方案,我会感到惊讶。

假设我有一个将列表或其他可迭代对象作为参数的方法或函数。 我希望对对象中的每个项目执行一次操作。

有时,只有一个项目(比如一个浮点值)被传递给这个函数。 在这种情况下,我的 for 循环不知道该怎么做。 因此,我发现自己在我的代码中添加了以下代码片段:

from collections.abc import Sequence

def my_function(value):
   if not isinstance(value, Sequence):
      value = [value]

   # rest of my function

这行得通,但它似乎很浪费而且不是特别清晰。 在搜索 StackOverflow 时,我还发现字符串被认为是序列,因此如果参数错误,这段代码很容易中断。 只是感觉不是正确的方法。

我来自 MATLAB 背景,由于标量被视为 1x1 矩阵,因此可以用该语言巧妙地解决这个问题。 至少,我希望有一个内置的东西,比如 numpy 的atleast_1d 函数,如果它不是一个,它会自动将任何东西转换为可迭代的。

简短的回答是不,没有简单的内置。 是的,如果您希望str (或bytes或类似字节的东西或其他)充当标量值,它会变得更丑陋。 Python 期望调用者遵守接口契约; 如果你说你接受序列,就这么说吧,它由调用者来包装任何单独的参数。

如果你必须这样做,有两种明显的方法可以做到:

首先是让您的函数接受可变参数而不是单个参数,并将其留给调用者解压缩任何序列,因此您始终可以迭代收到的可变参数:

def my_function(*values):
    for val in values:
        # Rest of function

具有单个项目的调用者使用my_function(a, b)调用您,具有序列的调用者使用my_function(*seq)调用您。 后者确实会产生一些开销来将序列解包到my_function接收的新元tuple ,但在许多情况下这很好。

如果由于某种原因这不可接受,另一种解决方案是推出自己的“确保可迭代”转换器功能,遵循您关心的任何规则:

from collections.abc import ByteString

def ensure_iterable(obj):
    if isinstance(obj, (str, ByteString)):
        return (obj,)  # Treat strings and bytes-like stuff as scalars and wrap
    try:
        iter(obj)  # Simplest way to test if something is iterable is to try to make it an iterator
    except TypeError:
        return (obj,)  # Not iterable, wrap
    else:
        return obj  # Already iterable

my_function可以用于:

def my_function(value):
   value = ensure_iterable(value)

Python 是一种通用语言,具有真正的标量以及列表等可迭代对象。

MATLAB 没有真正的标量。 基础对象是一个二维矩阵。 它并不是作为一种通用语言开始的。

numpy在 Python 中添加了类似 MATLAB 的数组,但它也可以有 0d 数组( scalar arrays ),这可能会让任性的 MATLAB 用户头疼。

许多numpy函数都有将其输入转换为数组的规定。 这样他们就可以使用列表输入和数组

In [10]: x = np.array(3)
In [11]: x
Out[11]: array(3)
In [12]: x.shape
Out[12]: ()
In [13]: for i in x: print(x)
Traceback (most recent call last):
  Input In [13] in <cell line: 1>
    for i in x: print(x)
TypeError: iteration over a 0-d array

它还具有确保数组为 1d 或 2 ...

In [14]: x = np.atleast_1d(1)
In [15]: x
Out[15]: array([1])
In [16]: for i in x: print(i)
1

但就像老式 MATLAB 一样,我们更愿意避免在numpy中进行迭代。 它没有jit编译,让当前的 MATLAB 用户可以通过迭代来解决问题。 从技术上讲, numpy函数确实使用迭代,但它通常在编译代码中。

np.sin应用于各种输入:

In [17]: np.sin(1)          # scalar
Out[17]: 0.8414709848078965
In [18]: np.sin([1,2,3])    # list
Out[18]: array([0.84147098, 0.90929743, 0.14112001])
In [19]: np.sin(np.array([1,2,3]).reshape(3,1))
Out[19]: 
array([[0.84147098],
       [0.90929743],
       [0.14112001]])

从技术上讲,[17] 结果是一个numpy scalar ,而不是基本的 python 浮点数:

In [20]: type(Out[17])
Out[20]: numpy.float64

我会鸭式:

def first(item):
    try:
        it=iter(item)
    except TypeError:
        it=iter([item])
    return next(it) 

测试它:

tests=[[1,2,3],'abc',1,1.23]

for e in tests:
    print(e, first(e))

印刷:

[1, 2, 3] 1
abc a
1 1
1.23 1.23

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM