简体   繁体   English

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

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

I come across this issue often, and I would be surprised if there wasn't some very simple and pythonic one-liner solution to it.我经常遇到这个问题,如果没有一些非常简单和 Pythonic 的单线解决方案,我会感到惊讶。

Suppose I have a method or a function that takes a list or some other iterable object as an argument.假设我有一个将列表或其他可迭代对象作为参数的方法或函数。 I want for an operation to be performed once for each item in the object.我希望对对象中的每个项目执行一次操作。

Sometimes, only a single item (say, a float value) is passed to this function.有时,只有一个项目(比如一个浮点值)被传递给这个函数。 In this situation, my for-loop doesn't know what to do.在这种情况下,我的 for 循环不知道该怎么做。 And so, I find myself peppering my code with the following snippet of code:因此,我发现自己在我的代码中添加了以下代码片段:

from collections.abc import Sequence

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

   # rest of my function

This works, but it seems wasteful and not particularly legible.这行得通,但它似乎很浪费而且不是特别清晰。 In searching StackOverflow I've also discovered that strings are considered sequences, and so this code could easily break given the wrong argument.在搜索 StackOverflow 时,我还发现字符串被认为是序列,因此如果参数错误,这段代码很容易中断。 It just doesn't feel like the right approach.只是感觉不是正确的方法。

I come from a MATLAB background, and this is neatly solved in that language since scalars are treated like 1x1 matrices.我来自 MATLAB 背景,由于标量被视为 1x1 矩阵,因此可以用该语言巧妙地解决这个问题。 I'd expect, at the very least, for there to be a built-in, something like numpy's atleast_1d function , that automatically converts anything into an iterable if it isn't one.至少,我希望有一个内置的东西,比如 numpy 的atleast_1d 函数,如果它不是一个,它会自动将任何东西转换为可迭代的。

The short answer is nope, there is no simple built-in.简短的回答是不,没有简单的内置。 And yep, if you want str (or bytes or bytes-like stuff or whatever) to act as a scalar value, it gets uglier.是的,如果您希望str (或bytes或类似字节的东西或其他)充当标量值,它会变得更丑陋。 Python expects callers to adhere to the interface contract; Python 期望调用者遵守接口契约; if you say you accept sequences, say so, and it's on the caller to wrap any individual arguments.如果你说你接受序列,就这么说吧,它由调用者来包装任何单独的参数。

If you must do this, there's two obvious ways to do it:如果你必须这样做,有两种明显的方法可以做到:

First is to make your function accept varargs instead of a single argument, and leave it up to the caller to unpack any sequences, so you can always iterate the varargs received:首先是让您的函数接受可变参数而不是单个参数,并将其留给调用者解压缩任何序列,因此您始终可以迭代收到的可变参数:

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

A caller with individual items calls you with my_function(a, b) , a caller with a sequence calls you with my_function(*seq) .具有单个项目的调用者使用my_function(a, b)调用您,具有序列的调用者使用my_function(*seq)调用您。 The latter does incur some overhead to unpack the sequence to a new tuple to be received by my_function , but in many cases this is fine.后者确实会产生一些开销来将序列解包到my_function接收的新元tuple ,但在许多情况下这很好。

If that's not acceptable for whatever reason, the other solution is to roll your own "ensure iterable" converter function, following whatever rules you care about:如果由于某种原因这不可接受,另一种解决方案是推出自己的“确保可迭代”转换器功能,遵循您关心的任何规则:

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

which my_function can use with: my_function可以用于:

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

Python is a general purpose language, with true scalars, and as well as iterables like lists. Python 是一种通用语言,具有真正的标量以及列表等可迭代对象。

MATLAB does not have true scalars. MATLAB 没有真正的标量。 The base object is a 2d matrix.基础对象是一个二维矩阵。 It did not start as a general purpose language.它并不是作为一种通用语言开始的。

numpy adds MATLAB like arrays to Python, but it too can have 0d arrays ( scalar arrays ), which may give the wayward MATLAB users headaches. numpy在 Python 中添加了类似 MATLAB 的数组,但它也可以有 0d 数组( scalar arrays ),这可能会让任性的 MATLAB 用户头疼。

Many numpy functions have a provision for converting their input to an array.许多numpy函数都有将其输入转换为数组的规定。 That way they will work a list input as well as array这样他们就可以使用列表输入和数组

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

It also has utility functions that insure the array is 1d, or 2 ...它还具有确保数组为 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

But like old-fashion MATLAB, we prefer to avoid iteration in numpy .但就像老式 MATLAB 一样,我们更愿意避免在numpy中进行迭代。 It doesn't have jit compilation that lets current MATLAB users get by with iterations.它没有jit编译,让当前的 MATLAB 用户可以通过迭代来解决问题。 Technically numpy functions do use iteration, but it usually in compiled code.从技术上讲, numpy函数确实使用迭代,但它通常在编译代码中。

np.sin applied to various inputs: 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]])

Technically, the [17] result is a numpy scalar , not a base python float:从技术上讲,[17] 结果是一个numpy scalar ,而不是基本的 python 浮点数:

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

I would duck type:我会鸭式:

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

Test it:测试它:

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

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

Prints:印刷:

[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