就地修改嵌套列表元素的最优雅的方法

发布于 2024-11-01 16:24:46 字数 467 浏览 2 评论 0原文

我有一个看起来像这样的二维列表:

table = [['donkey', '2', '1', '0'], ['goat', '5', '3', '2']]

我想将最后三个元素更改为整数,但下面的代码感觉非常丑陋:

for row in table:
    for i in range(len(row)-1):
        row[i+1] = int(row[i+1])

但我宁愿有看起来像这样的东西:

for row in table:
    for col in row[1:]:
        col = int(col)

我认为应该有一种方法来编写上面的代码,但是切片创建了一个与原始列表分开的迭代器/新列表,因此引用不会保留。

有什么方法可以得到更Pythonic的解决方案吗?

I have a 2D list that looks like this:

table = [['donkey', '2', '1', '0'], ['goat', '5', '3', '2']]

I want to change the last three elements to integers, but the code below feels very ugly:

for row in table:
    for i in range(len(row)-1):
        row[i+1] = int(row[i+1])

But I'd rather have something that looks like:

for row in table:
    for col in row[1:]:
        col = int(col)

I think there should be a way to write the code above, but the slice creates an iterator/new list that's separate from the original, so the references don't carry over.

Is there some way to get a more Pythonic solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

雨巷深深 2024-11-08 16:24:46
for row in table:
    row[1:] = [int(c) for c in row[1:]]

上面看起来是不是更Pythonic?

for row in table:
    row[1:] = [int(c) for c in row[1:]]

Does above look more pythonic?

夜光 2024-11-08 16:24:46

尝试:

>>> for row in table:
...     row[1:]=map(int,row[1:])
... 
>>> table
[['donkey', 2, 1, 0], ['goat', 5, 3, 2]]

据我所知,分配给 list 切片会强制操作就地完成,而不是创建新的 list

Try:

>>> for row in table:
...     row[1:]=map(int,row[1:])
... 
>>> table
[['donkey', 2, 1, 0], ['goat', 5, 3, 2]]

AFAIK, assigning to a list slice forces the operation to be done in place instead of creating a new list.

想念有你 2024-11-08 16:24:46

我非常喜欢谢卡尔的回答。

作为一般规则,在编写 Python 代码时,如果您发现自己在 range(len(somelist)) 中编写了 f或 i,那么您就做错了:

  • 尝试 enumerate如果您有一个列表,
  • 请尝试 zipitertools.izip 如果您有 2 个或更多要并行迭代的列表

在您的情况下,第一列是不同的,所以你不能优雅地使用枚举:

for row in table:
    for i, val in enumerate(row):
        if i == 0: continue
        row[i] = int(val)

I like Shekhar answer a lot.

As a general rule, when writing Python code, if you find yourself writing for i in range(len(somelist)), you're doing it wrong:

  • try enumerate if you have a single list
  • try zip or itertools.izip if you have 2 or more lists you want to iterate on in parallel

In your case, the first column is different so you cannot elegantly use enumerate:

for row in table:
    for i, val in enumerate(row):
        if i == 0: continue
        row[i] = int(val)
亣腦蒛氧 2024-11-08 16:24:46

您的“丑陋”代码可以通过使用两个参数调用 range 来改进:

for row in table:
    for i in range(1, len(row)):
        row[i] = int(row[i])

如果您坚持在适当的位置更改项目而不分配新的临时列表(或者通过使用列表理解、map 和/或切片)。请参阅 是否有相当于“map”的就地 虽然我不推荐这样做,但

您也可以通过引入自己的就地映射函数来使此代码更通用:

def inplacemap(f, items, start=0, end=None):
    """Applies ``f`` to each item in the iterable ``items`` between the range
    ``start`` and ``end``."""
    # If end was not specified, make it the length of the iterable
    # We avoid setting end in the parameter list to force it to be evaluated on
    # each invocation
    if end is None:
        end = len(items)
    for i in range(start, end):
        items[i] = f(items[i])

for row in table:
    inplacemap(int, row, 1)

就我个人而言,我发现这更少Pythonic。最好只有一种明显的方法可以做到这一点,但这不是。

Your "ugly" code can be improved just by calling range with two arguments:

for row in table:
    for i in range(1, len(row)):
        row[i] = int(row[i])

This is probably the best you can do if you insist on changing the items in place without allocating new temporary lists (either by using a list comprehension, map, and/or slicing). See Is there an in-place equivalent to 'map' in python?

Although I don't recommend it, you can also make this code more general by introducing your own in-place map function:

def inplacemap(f, items, start=0, end=None):
    """Applies ``f`` to each item in the iterable ``items`` between the range
    ``start`` and ``end``."""
    # If end was not specified, make it the length of the iterable
    # We avoid setting end in the parameter list to force it to be evaluated on
    # each invocation
    if end is None:
        end = len(items)
    for i in range(start, end):
        items[i] = f(items[i])

for row in table:
    inplacemap(int, row, 1)

Personally, I find this less Pythonic. There is preferably only one obvious way to do it, and this isn't it.

铜锣湾横着走 2024-11-08 16:24:46

使用列表理解:

table = [row[0] + [int(col) for col in row[1:]] for row in table]

Use list comprehensions:

table = [row[0] + [int(col) for col in row[1:]] for row in table]
二货你真萌 2024-11-08 16:24:46

这将起作用:

table = [[row[0]] + [int(v) for v in row[1:]] for row in table]

但是您可能需要考虑在首次创建列表时进行转换。

This will work:

table = [[row[0]] + [int(v) for v in row[1:]] for row in table]

However you might want to think about doing the conversion at the point where the list is first created.

俯瞰星空 2024-11-08 16:24:46

这实现了您正在寻找的内容。这是一个可读的解决方案。您也可以使用 listcomp 来获得类似的结果。

>>> for row in table:
...     for i, elem in enumerate(row):
...             try:
...                     int(elem)
...             except ValueError:
...                     pass
...             else:
...                     row[i] = int(elem)
... 

This accomplishes what you are looking for. It is a readable solution. You can go for similar one using listcomp too.

>>> for row in table:
...     for i, elem in enumerate(row):
...             try:
...                     int(elem)
...             except ValueError:
...                     pass
...             else:
...                     row[i] = int(elem)
... 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文