标签归档:reverse

如何以相反的顺序读取文件?

问题:如何以相反的顺序读取文件?

如何使用python以相反的顺序读取文件?我想从最后一行读取文件。

How to read a file in reverse order using python? I want to read a file from last line to first line.


回答 0

for line in reversed(open("filename").readlines()):
    print line.rstrip()

在Python 3中:

for line in reversed(list(open("filename"))):
    print(line.rstrip())
for line in reversed(open("filename").readlines()):
    print line.rstrip()

And in Python 3:

for line in reversed(list(open("filename"))):
    print(line.rstrip())

回答 1

作为生成器编写的正确,有效的答案。

import os

def reverse_readline(filename, buf_size=8192):
    """A generator that returns the lines of a file in reverse order"""
    with open(filename) as fh:
        segment = None
        offset = 0
        fh.seek(0, os.SEEK_END)
        file_size = remaining_size = fh.tell()
        while remaining_size > 0:
            offset = min(file_size, offset + buf_size)
            fh.seek(file_size - offset)
            buffer = fh.read(min(remaining_size, buf_size))
            remaining_size -= buf_size
            lines = buffer.split('\n')
            # The first line of the buffer is probably not a complete line so
            # we'll save it and append it to the last line of the next buffer
            # we read
            if segment is not None:
                # If the previous chunk starts right from the beginning of line
                # do not concat the segment to the last line of new chunk.
                # Instead, yield the segment first 
                if buffer[-1] != '\n':
                    lines[-1] += segment
                else:
                    yield segment
            segment = lines[0]
            for index in range(len(lines) - 1, 0, -1):
                if lines[index]:
                    yield lines[index]
        # Don't yield None if the file was empty
        if segment is not None:
            yield segment

A correct, efficient answer written as a generator.

import os

def reverse_readline(filename, buf_size=8192):
    """A generator that returns the lines of a file in reverse order"""
    with open(filename) as fh:
        segment = None
        offset = 0
        fh.seek(0, os.SEEK_END)
        file_size = remaining_size = fh.tell()
        while remaining_size > 0:
            offset = min(file_size, offset + buf_size)
            fh.seek(file_size - offset)
            buffer = fh.read(min(remaining_size, buf_size))
            remaining_size -= buf_size
            lines = buffer.split('\n')
            # The first line of the buffer is probably not a complete line so
            # we'll save it and append it to the last line of the next buffer
            # we read
            if segment is not None:
                # If the previous chunk starts right from the beginning of line
                # do not concat the segment to the last line of new chunk.
                # Instead, yield the segment first 
                if buffer[-1] != '\n':
                    lines[-1] += segment
                else:
                    yield segment
            segment = lines[0]
            for index in range(len(lines) - 1, 0, -1):
                if lines[index]:
                    yield lines[index]
        # Don't yield None if the file was empty
        if segment is not None:
            yield segment

回答 2

这样的事情怎么样:

import os


def readlines_reverse(filename):
    with open(filename) as qfile:
        qfile.seek(0, os.SEEK_END)
        position = qfile.tell()
        line = ''
        while position >= 0:
            qfile.seek(position)
            next_char = qfile.read(1)
            if next_char == "\n":
                yield line[::-1]
                line = ''
            else:
                line += next_char
            position -= 1
        yield line[::-1]


if __name__ == '__main__':
    for qline in readlines_reverse(raw_input()):
        print qline

由于文件是按相反的顺序逐个字符读取的,因此即使在非常大的文件中也可以使用,只要将单独的行放入内存中即可。

How about something like this:

import os


def readlines_reverse(filename):
    with open(filename) as qfile:
        qfile.seek(0, os.SEEK_END)
        position = qfile.tell()
        line = ''
        while position >= 0:
            qfile.seek(position)
            next_char = qfile.read(1)
            if next_char == "\n":
                yield line[::-1]
                line = ''
            else:
                line += next_char
            position -= 1
        yield line[::-1]


if __name__ == '__main__':
    for qline in readlines_reverse(raw_input()):
        print qline

Since the file is read character by character in reverse order, it will work even on very large files, as long as individual lines fit into memory.


回答 3

您也可以使用python模块 file_read_backwards

通过pip install file_read_backwards(v1.2.1)安装后,您可以通过以下方式以内存高效的方式向后(逐行)读取整个文件:

#!/usr/bin/env python2.7

from file_read_backwards import FileReadBackwards

with FileReadBackwards("/path/to/file", encoding="utf-8") as frb:
    for l in frb:
         print l

它支持“ utf-8”,“ latin-1”和“ ascii”编码。

也支持python3。可以在http://file-read-backwards.readthedocs.io/en/latest/readme.html上找到更多文档。

You can also use python module file_read_backwards.

After installing it, via pip install file_read_backwards (v1.2.1), you can read the entire file backwards (line-wise) in a memory efficient manner via:

#!/usr/bin/env python2.7

from file_read_backwards import FileReadBackwards

with FileReadBackwards("/path/to/file", encoding="utf-8") as frb:
    for l in frb:
         print l

It supports “utf-8″,”latin-1”, and “ascii” encodings.

Support is also available for python3. Further documentation can be found at http://file-read-backwards.readthedocs.io/en/latest/readme.html


回答 4

for line in reversed(open("file").readlines()):
    print line.rstrip()

如果您使用的是Linux,则可以使用taccommand。

$ tac file

您可以在ActiveState的此处此处找到2个食谱

for line in reversed(open("file").readlines()):
    print line.rstrip()

If you are on linux, you can use tac command.

$ tac file

2 recipes you can find in ActiveState here and here


回答 5

import re

def filerev(somefile, buffer=0x20000):
  somefile.seek(0, os.SEEK_END)
  size = somefile.tell()
  lines = ['']
  rem = size % buffer
  pos = max(0, (size // buffer - 1) * buffer)
  while pos >= 0:
    somefile.seek(pos, os.SEEK_SET)
    data = somefile.read(rem + buffer) + lines[0]
    rem = 0
    lines = re.findall('[^\n]*\n?', data)
    ix = len(lines) - 2
    while ix > 0:
      yield lines[ix]
      ix -= 1
    pos -= buffer
  else:
    yield lines[0]

with open(sys.argv[1], 'r') as f:
  for line in filerev(f):
    sys.stdout.write(line)
import re

def filerev(somefile, buffer=0x20000):
  somefile.seek(0, os.SEEK_END)
  size = somefile.tell()
  lines = ['']
  rem = size % buffer
  pos = max(0, (size // buffer - 1) * buffer)
  while pos >= 0:
    somefile.seek(pos, os.SEEK_SET)
    data = somefile.read(rem + buffer) + lines[0]
    rem = 0
    lines = re.findall('[^\n]*\n?', data)
    ix = len(lines) - 2
    while ix > 0:
      yield lines[ix]
      ix -= 1
    pos -= buffer
  else:
    yield lines[0]

with open(sys.argv[1], 'r') as f:
  for line in filerev(f):
    sys.stdout.write(line)

回答 6

接受的答案不适用于文件大而内存不足的情况(这种情况很少见)。

正如其他人所指出的那样,@ srohde的答案看起来不错,但存在下一个问题:

  • 当我们可以传递文件对象并将其留给用户来决定应以哪种编码方式读取文件时,打开文件看起来是多余的,
  • 即使我们重构为接受文件对象,它也不适用于所有编码:我们可以选择具有utf-8编码和非ascii内容的文件,例如

    й

    通过buf_size等于1并将具有

    UnicodeDecodeError: 'utf8' codec can't decode byte 0xb9 in position 0: invalid start byte

    当然文字可能更大,但 buf_size可能会被拾取,从而导致上述混淆错误,

  • 我们无法指定自定义行分隔符,
  • 我们不能选择保留行分隔符。

因此,考虑到所有这些问题,我编写了单独的函数:

  • 一种适用于字节流的
  • 第二个用于文本流,并将其基础字节流委托给第一个,并解码结果行。

首先,让我们定义下一个实用程序函数:

ceil_division用于天花板分隔(与标准//地板分隔相反,更多信息可以在此线程中找到)

def ceil_division(left_number, right_number):
    """
    Divides given numbers with ceiling.
    """
    return -(-left_number // right_number)

split 通过从右端给定的分隔符分割字符串并保持其能力:

def split(string, separator, keep_separator):
    """
    Splits given string by given separator.
    """
    parts = string.split(separator)
    if keep_separator:
        *parts, last_part = parts
        parts = [part + separator for part in parts]
        if last_part:
            return parts + [last_part]
    return parts

read_batch_from_end 从二进制流的右端读取批处理

def read_batch_from_end(byte_stream, size, end_position):
    """
    Reads batch from the end of given byte stream.
    """
    if end_position > size:
        offset = end_position - size
    else:
        offset = 0
        size = end_position
    byte_stream.seek(offset)
    return byte_stream.read(size)

之后,我们可以定义函数以相反的顺序读取字节流,例如

import functools
import itertools
import os
from operator import methodcaller, sub


def reverse_binary_stream(byte_stream, batch_size=None,
                          lines_separator=None,
                          keep_lines_separator=True):
    if lines_separator is None:
        lines_separator = (b'\r', b'\n', b'\r\n')
        lines_splitter = methodcaller(str.splitlines.__name__,
                                      keep_lines_separator)
    else:
        lines_splitter = functools.partial(split,
                                           separator=lines_separator,
                                           keep_separator=keep_lines_separator)
    stream_size = byte_stream.seek(0, os.SEEK_END)
    if batch_size is None:
        batch_size = stream_size or 1
    batches_count = ceil_division(stream_size, batch_size)
    remaining_bytes_indicator = itertools.islice(
            itertools.accumulate(itertools.chain([stream_size],
                                                 itertools.repeat(batch_size)),
                                 sub),
            batches_count)
    try:
        remaining_bytes_count = next(remaining_bytes_indicator)
    except StopIteration:
        return

    def read_batch(position):
        result = read_batch_from_end(byte_stream,
                                     size=batch_size,
                                     end_position=position)
        while result.startswith(lines_separator):
            try:
                position = next(remaining_bytes_indicator)
            except StopIteration:
                break
            result = (read_batch_from_end(byte_stream,
                                          size=batch_size,
                                          end_position=position)
                      + result)
        return result

    batch = read_batch(remaining_bytes_count)
    segment, *lines = lines_splitter(batch)
    yield from reverse(lines)
    for remaining_bytes_count in remaining_bytes_indicator:
        batch = read_batch(remaining_bytes_count)
        lines = lines_splitter(batch)
        if batch.endswith(lines_separator):
            yield segment
        else:
            lines[-1] += segment
        segment, *lines = lines
        yield from reverse(lines)
    yield segment

最后,可以将文本文件反转功能定义如下:

import codecs


def reverse_file(file, batch_size=None, 
                 lines_separator=None,
                 keep_lines_separator=True):
    encoding = file.encoding
    if lines_separator is not None:
        lines_separator = lines_separator.encode(encoding)
    yield from map(functools.partial(codecs.decode,
                                     encoding=encoding),
                   reverse_binary_stream(
                           file.buffer,
                           batch_size=batch_size,
                           lines_separator=lines_separator,
                           keep_lines_separator=keep_lines_separator))

测验

准备工作

我已经使用fsutilcommand生成了4个文件:

  1. 没有内容的empty.txt,大小为0MB
  2. tiny.txt,大小为1MB
  3. small.txt,大小为10MB
  4. large.txt,大小为50MB

我也重构了@srohde解决方案以使用文件对象而不是文件路径。

测试脚本

from timeit import Timer

repeats_count = 7
number = 1
create_setup = ('from collections import deque\n'
                'from __main__ import reverse_file, reverse_readline\n'
                'file = open("{}")').format
srohde_solution = ('with file:\n'
                   '    deque(reverse_readline(file,\n'
                   '                           buf_size=8192),'
                   '          maxlen=0)')
azat_ibrakov_solution = ('with file:\n'
                         '    deque(reverse_file(file,\n'
                         '                       lines_separator="\\n",\n'
                         '                       keep_lines_separator=False,\n'
                         '                       batch_size=8192), maxlen=0)')
print('reversing empty file by "srohde"',
      min(Timer(srohde_solution,
                create_setup('empty.txt')).repeat(repeats_count, number)))
print('reversing empty file by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('empty.txt')).repeat(repeats_count, number)))
print('reversing tiny file (1MB) by "srohde"',
      min(Timer(srohde_solution,
                create_setup('tiny.txt')).repeat(repeats_count, number)))
print('reversing tiny file (1MB) by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('tiny.txt')).repeat(repeats_count, number)))
print('reversing small file (10MB) by "srohde"',
      min(Timer(srohde_solution,
                create_setup('small.txt')).repeat(repeats_count, number)))
print('reversing small file (10MB) by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('small.txt')).repeat(repeats_count, number)))
print('reversing large file (50MB) by "srohde"',
      min(Timer(srohde_solution,
                create_setup('large.txt')).repeat(repeats_count, number)))
print('reversing large file (50MB) by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('large.txt')).repeat(repeats_count, number)))

注意:我已经使用了collections.deque类来耗尽生成器。

产出

对于Windows 10上的PyPy 3.5:

reversing empty file by "srohde" 8.31e-05
reversing empty file by "Azat Ibrakov" 0.00016090000000000028
reversing tiny file (1MB) by "srohde" 0.160081
reversing tiny file (1MB) by "Azat Ibrakov" 0.09594989999999998
reversing small file (10MB) by "srohde" 8.8891863
reversing small file (10MB) by "Azat Ibrakov" 5.323388100000001
reversing large file (50MB) by "srohde" 186.5338368
reversing large file (50MB) by "Azat Ibrakov" 99.07450229999998

对于Windows 10上的CPython 3.5:

reversing empty file by "srohde" 3.600000000000001e-05
reversing empty file by "Azat Ibrakov" 4.519999999999958e-05
reversing tiny file (1MB) by "srohde" 0.01965560000000001
reversing tiny file (1MB) by "Azat Ibrakov" 0.019207699999999994
reversing small file (10MB) by "srohde" 3.1341862999999996
reversing small file (10MB) by "Azat Ibrakov" 3.0872588000000007
reversing large file (50MB) by "srohde" 82.01206720000002
reversing large file (50MB) by "Azat Ibrakov" 82.16775059999998

因此,我们可以看到它的性能像原始解决方案一样,但它更具通用性,并且没有上面列出的缺点。


广告

我已经将此添加到具有许多经过良好测试的功能/迭代实用程序0.3.0lz软件包版本(需要Python 3.5 +)中。

可以像

 import io
 from lz.iterating import reverse
 ...
 with open('path/to/file') as file:
     for line in reverse(file, batch_size=io.DEFAULT_BUFFER_SIZE):
         print(line)

它支持所有标准编码(可能utf-7是因为我很难定义一种策略来生成可编码的字符串)。

Accepted answer won’t work for cases with large files that won’t fit in memory (which is not a rare case).

As it was noted by others, @srohde answer looks good, but it has next issues:

  • openning file looks redundant, when we can pass file object & leave it to user to decide in which encoding it should be read,
  • even if we refactor to accept file object, it won’t work for all encodings: we can choose file with utf-8 encoding and non-ascii contents like

    й
    

    pass buf_size equal to 1 and will have

    UnicodeDecodeError: 'utf8' codec can't decode byte 0xb9 in position 0: invalid start byte
    

    of course text may be larger but buf_size may be picked up so it’ll lead to obfuscated error like above,

  • we can’t specify custom line separator,
  • we can’t choose to keep line separator.

So considering all these concerns I’ve written separate functions:

  • one which works with byte streams,
  • second one which works with text streams and delegates its underlying byte stream to the first one and decodes resulting lines.

First of all let’s define next utility functions:

ceil_division for making division with ceiling (in contrast with standard // division with floor, more info can be found in this thread)

def ceil_division(left_number, right_number):
    """
    Divides given numbers with ceiling.
    """
    return -(-left_number // right_number)

split for splitting string by given separator from right end with ability to keep it:

def split(string, separator, keep_separator):
    """
    Splits given string by given separator.
    """
    parts = string.split(separator)
    if keep_separator:
        *parts, last_part = parts
        parts = [part + separator for part in parts]
        if last_part:
            return parts + [last_part]
    return parts

read_batch_from_end to read batch from the right end of binary stream

def read_batch_from_end(byte_stream, size, end_position):
    """
    Reads batch from the end of given byte stream.
    """
    if end_position > size:
        offset = end_position - size
    else:
        offset = 0
        size = end_position
    byte_stream.seek(offset)
    return byte_stream.read(size)

After that we can define function for reading byte stream in reverse order like

import functools
import itertools
import os
from operator import methodcaller, sub


def reverse_binary_stream(byte_stream, batch_size=None,
                          lines_separator=None,
                          keep_lines_separator=True):
    if lines_separator is None:
        lines_separator = (b'\r', b'\n', b'\r\n')
        lines_splitter = methodcaller(str.splitlines.__name__,
                                      keep_lines_separator)
    else:
        lines_splitter = functools.partial(split,
                                           separator=lines_separator,
                                           keep_separator=keep_lines_separator)
    stream_size = byte_stream.seek(0, os.SEEK_END)
    if batch_size is None:
        batch_size = stream_size or 1
    batches_count = ceil_division(stream_size, batch_size)
    remaining_bytes_indicator = itertools.islice(
            itertools.accumulate(itertools.chain([stream_size],
                                                 itertools.repeat(batch_size)),
                                 sub),
            batches_count)
    try:
        remaining_bytes_count = next(remaining_bytes_indicator)
    except StopIteration:
        return

    def read_batch(position):
        result = read_batch_from_end(byte_stream,
                                     size=batch_size,
                                     end_position=position)
        while result.startswith(lines_separator):
            try:
                position = next(remaining_bytes_indicator)
            except StopIteration:
                break
            result = (read_batch_from_end(byte_stream,
                                          size=batch_size,
                                          end_position=position)
                      + result)
        return result

    batch = read_batch(remaining_bytes_count)
    segment, *lines = lines_splitter(batch)
    yield from reverse(lines)
    for remaining_bytes_count in remaining_bytes_indicator:
        batch = read_batch(remaining_bytes_count)
        lines = lines_splitter(batch)
        if batch.endswith(lines_separator):
            yield segment
        else:
            lines[-1] += segment
        segment, *lines = lines
        yield from reverse(lines)
    yield segment

and finally a function for reversing text file can be defined like:

import codecs


def reverse_file(file, batch_size=None, 
                 lines_separator=None,
                 keep_lines_separator=True):
    encoding = file.encoding
    if lines_separator is not None:
        lines_separator = lines_separator.encode(encoding)
    yield from map(functools.partial(codecs.decode,
                                     encoding=encoding),
                   reverse_binary_stream(
                           file.buffer,
                           batch_size=batch_size,
                           lines_separator=lines_separator,
                           keep_lines_separator=keep_lines_separator))

Tests

Preparations

I’ve generated 4 files using fsutil command:

  1. empty.txt with no contents, size 0MB
  2. tiny.txt with size of 1MB
  3. small.txt with size of 10MB
  4. large.txt with size of 50MB

also I’ve refactored @srohde solution to work with file object instead of file path.

Test script

from timeit import Timer

repeats_count = 7
number = 1
create_setup = ('from collections import deque\n'
                'from __main__ import reverse_file, reverse_readline\n'
                'file = open("{}")').format
srohde_solution = ('with file:\n'
                   '    deque(reverse_readline(file,\n'
                   '                           buf_size=8192),'
                   '          maxlen=0)')
azat_ibrakov_solution = ('with file:\n'
                         '    deque(reverse_file(file,\n'
                         '                       lines_separator="\\n",\n'
                         '                       keep_lines_separator=False,\n'
                         '                       batch_size=8192), maxlen=0)')
print('reversing empty file by "srohde"',
      min(Timer(srohde_solution,
                create_setup('empty.txt')).repeat(repeats_count, number)))
print('reversing empty file by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('empty.txt')).repeat(repeats_count, number)))
print('reversing tiny file (1MB) by "srohde"',
      min(Timer(srohde_solution,
                create_setup('tiny.txt')).repeat(repeats_count, number)))
print('reversing tiny file (1MB) by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('tiny.txt')).repeat(repeats_count, number)))
print('reversing small file (10MB) by "srohde"',
      min(Timer(srohde_solution,
                create_setup('small.txt')).repeat(repeats_count, number)))
print('reversing small file (10MB) by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('small.txt')).repeat(repeats_count, number)))
print('reversing large file (50MB) by "srohde"',
      min(Timer(srohde_solution,
                create_setup('large.txt')).repeat(repeats_count, number)))
print('reversing large file (50MB) by "Azat Ibrakov"',
      min(Timer(azat_ibrakov_solution,
                create_setup('large.txt')).repeat(repeats_count, number)))

Note: I’ve used collections.deque class to exhaust generator.

Outputs

For PyPy 3.5 on Windows 10:

reversing empty file by "srohde" 8.31e-05
reversing empty file by "Azat Ibrakov" 0.00016090000000000028
reversing tiny file (1MB) by "srohde" 0.160081
reversing tiny file (1MB) by "Azat Ibrakov" 0.09594989999999998
reversing small file (10MB) by "srohde" 8.8891863
reversing small file (10MB) by "Azat Ibrakov" 5.323388100000001
reversing large file (50MB) by "srohde" 186.5338368
reversing large file (50MB) by "Azat Ibrakov" 99.07450229999998

For CPython 3.5 on Windows 10:

reversing empty file by "srohde" 3.600000000000001e-05
reversing empty file by "Azat Ibrakov" 4.519999999999958e-05
reversing tiny file (1MB) by "srohde" 0.01965560000000001
reversing tiny file (1MB) by "Azat Ibrakov" 0.019207699999999994
reversing small file (10MB) by "srohde" 3.1341862999999996
reversing small file (10MB) by "Azat Ibrakov" 3.0872588000000007
reversing large file (50MB) by "srohde" 82.01206720000002
reversing large file (50MB) by "Azat Ibrakov" 82.16775059999998

So as we can see it performs like original solution, but is more general and free of its disadvantages listed above.


Advertisement

I’ve added this to 0.3.0 version of lz package (requires Python 3.5+) that have many well-tested functional/iterating utilities.

Can be used like

 import io
 from lz.iterating import reverse
 ...
 with open('path/to/file') as file:
     for line in reverse(file, batch_size=io.DEFAULT_BUFFER_SIZE):
         print(line)

It supports all standard encodings (maybe except utf-7 since it is hard for me to define a strategy for generating strings encodable with it).


回答 7

在这里,您可以找到我的实现,可以通过更改“ buffer”变量来限制ram的使用,这是一个程序在开始时打印空行的错误。

如果没有新行超过缓冲区字节,内存使用也会增加,“ leak”变量将一直增加,直到看到新行(“ \ n”)为止。

这也适用于大于我的总内存的16 GB文件。

import os,sys
buffer = 1024*1024 # 1MB
f = open(sys.argv[1])
f.seek(0, os.SEEK_END)
filesize = f.tell()

division, remainder = divmod(filesize, buffer)
line_leak=''

for chunk_counter in range(1,division + 2):
    if division - chunk_counter < 0:
        f.seek(0, os.SEEK_SET)
        chunk = f.read(remainder)
    elif division - chunk_counter >= 0:
        f.seek(-(buffer*chunk_counter), os.SEEK_END)
        chunk = f.read(buffer)

    chunk_lines_reversed = list(reversed(chunk.split('\n')))
    if line_leak: # add line_leak from previous chunk to beginning
        chunk_lines_reversed[0] += line_leak

    # after reversed, save the leakedline for next chunk iteration
    line_leak = chunk_lines_reversed.pop()

    if chunk_lines_reversed:
        print "\n".join(chunk_lines_reversed)
    # print the last leaked line
    if division - chunk_counter < 0:
        print line_leak

Here you can find my my implementation, you can limit the ram usage by changing the “buffer” variable, there is a bug that the program prints an empty line in the beginning.

And also ram usage may be increase if there is no new lines for more than buffer bytes, “leak” variable will increase until seeing a new line (“\n”).

This is also working for 16 GB files which is bigger then my total memory.

import os,sys
buffer = 1024*1024 # 1MB
f = open(sys.argv[1])
f.seek(0, os.SEEK_END)
filesize = f.tell()

division, remainder = divmod(filesize, buffer)
line_leak=''

for chunk_counter in range(1,division + 2):
    if division - chunk_counter < 0:
        f.seek(0, os.SEEK_SET)
        chunk = f.read(remainder)
    elif division - chunk_counter >= 0:
        f.seek(-(buffer*chunk_counter), os.SEEK_END)
        chunk = f.read(buffer)

    chunk_lines_reversed = list(reversed(chunk.split('\n')))
    if line_leak: # add line_leak from previous chunk to beginning
        chunk_lines_reversed[0] += line_leak

    # after reversed, save the leakedline for next chunk iteration
    line_leak = chunk_lines_reversed.pop()

    if chunk_lines_reversed:
        print "\n".join(chunk_lines_reversed)
    # print the last leaked line
    if division - chunk_counter < 0:
        print line_leak

回答 8

感谢您的回答@srohde。它使用“ is”运算符检查换行符时有一个小错误,并且我无法对信誉为1的答案进行评论。我也想管理外部文件的打开,因为这使我可以将杂物嵌入luigi任务。

我需要更改的形式如下:

with open(filename) as fp:
    for line in fp:
        #print line,  # contains new line
        print '>{}<'.format(line)

我想更改为:

with open(filename) as fp:
    for line in reversed_fp_iter(fp, 4):
        #print line,  # contains new line
        print '>{}<'.format(line)

这是修改后的答案,需要文件句柄并保留换行符:

def reversed_fp_iter(fp, buf_size=8192):
    """a generator that returns the lines of a file in reverse order
    ref: https://stackoverflow.com/a/23646049/8776239
    """
    segment = None  # holds possible incomplete segment at the beginning of the buffer
    offset = 0
    fp.seek(0, os.SEEK_END)
    file_size = remaining_size = fp.tell()
    while remaining_size > 0:
        offset = min(file_size, offset + buf_size)
        fp.seek(file_size - offset)
        buffer = fp.read(min(remaining_size, buf_size))
        remaining_size -= buf_size
        lines = buffer.splitlines(True)
        # the first line of the buffer is probably not a complete line so
        # we'll save it and append it to the last line of the next buffer
        # we read
        if segment is not None:
            # if the previous chunk starts right from the beginning of line
            # do not concat the segment to the last line of new chunk
            # instead, yield the segment first
            if buffer[-1] == '\n':
                #print 'buffer ends with newline'
                yield segment
            else:
                lines[-1] += segment
                #print 'enlarged last line to >{}<, len {}'.format(lines[-1], len(lines))
        segment = lines[0]
        for index in range(len(lines) - 1, 0, -1):
            if len(lines[index]):
                yield lines[index]
    # Don't yield None if the file was empty
    if segment is not None:
        yield segment

Thanks for the answer @srohde. It has a small bug checking for newline character with ‘is’ operator, and I could not comment on the answer with 1 reputation. Also I’d like to manage file open outside because that enables me to embed my ramblings for luigi tasks.

What I needed to change has the form:

with open(filename) as fp:
    for line in fp:
        #print line,  # contains new line
        print '>{}<'.format(line)

I’d love to change to:

with open(filename) as fp:
    for line in reversed_fp_iter(fp, 4):
        #print line,  # contains new line
        print '>{}<'.format(line)

Here is a modified answer that wants a file handle and keeps newlines:

def reversed_fp_iter(fp, buf_size=8192):
    """a generator that returns the lines of a file in reverse order
    ref: https://stackoverflow.com/a/23646049/8776239
    """
    segment = None  # holds possible incomplete segment at the beginning of the buffer
    offset = 0
    fp.seek(0, os.SEEK_END)
    file_size = remaining_size = fp.tell()
    while remaining_size > 0:
        offset = min(file_size, offset + buf_size)
        fp.seek(file_size - offset)
        buffer = fp.read(min(remaining_size, buf_size))
        remaining_size -= buf_size
        lines = buffer.splitlines(True)
        # the first line of the buffer is probably not a complete line so
        # we'll save it and append it to the last line of the next buffer
        # we read
        if segment is not None:
            # if the previous chunk starts right from the beginning of line
            # do not concat the segment to the last line of new chunk
            # instead, yield the segment first
            if buffer[-1] == '\n':
                #print 'buffer ends with newline'
                yield segment
            else:
                lines[-1] += segment
                #print 'enlarged last line to >{}<, len {}'.format(lines[-1], len(lines))
        segment = lines[0]
        for index in range(len(lines) - 1, 0, -1):
            if len(lines[index]):
                yield lines[index]
    # Don't yield None if the file was empty
    if segment is not None:
        yield segment

回答 9

一个简单的函数来创建另一个反转的文件(仅适用于Linux):

import os
def tac(file1, file2):
     print(os.system('tac %s > %s' % (file1,file2)))

如何使用

tac('ordered.csv', 'reversed.csv')
f = open('reversed.csv')

a simple function to create a second file reversed (linux only):

import os
def tac(file1, file2):
     print(os.system('tac %s > %s' % (file1,file2)))

how to use

tac('ordered.csv', 'reversed.csv')
f = open('reversed.csv')

回答 10

如果您担心文件大小/内存使用情况,则可以对文件进行内存映射并向后扫描换行符,这是一种解决方案:

如何在文本文件中搜索字符串?

If you are concerned about file size / memory usage, memory-mapping the file and scanning backwards for newlines is a solution:

How to search for a string in text files?


回答 11

使用open(“ filename”)as f:

    print(f.read()[::-1])

with open(“filename”) as f:

    print(f.read()[::-1])

回答 12

def reverse_lines(filename):
    y=open(filename).readlines()
    return y[::-1]
def reverse_lines(filename):
    y=open(filename).readlines()
    return y[::-1]

回答 13

with处理文件时请务必使用,因为它可以为您处理所有事情:

with open('filename', 'r') as f:
    for line in reversed(f.readlines()):
        print line

或在Python 3中:

with open('filename', 'r') as f:
    for line in reversed(list(f.readlines())):
        print(line)

Always use with when working with files as it handles everything for you:

with open('filename', 'r') as f:
    for line in reversed(f.readlines()):
        print line

Or in Python 3:

with open('filename', 'r') as f:
    for line in reversed(list(f.readlines())):
        print(line)

回答 14

您需要首先以读取格式打开文件,将其保存到变量,然后以写入格式打开第二个文件,在该文件中您将使用[::-1]切片来写入或附加变量,从而完全反转文件。您还可以使用readlines()使其成为一行列表,您可以对其进行操作

def copy_and_reverse(filename, newfile):
    with open(filename) as file:
        text = file.read()
    with open(newfile, "w") as file2:
        file2.write(text[::-1])

you would need to first open your file in read format, save it to a variable, then open the second file in write format where you would write or append the variable using a the [::-1] slice, completely reversing the file. You can also use readlines() to make it into a list of lines, which you can manipulate

def copy_and_reverse(filename, newfile):
    with open(filename) as file:
        text = file.read()
    with open(newfile, "w") as file2:
        file2.write(text[::-1])

回答 15

大多数答案都需要在执行任何操作之前先读取整个文件。此样本从头开始读取越来越大的样本

在编写此答案时,我仅看到MuratYükselen的答案。几乎一样,我认为这是一件好事。下面的示例还处理\ r,并在每个步骤中增加其缓冲区大小。我也有一些单元测试来备份此代码。

def readlines_reversed(f):
    """ Iterate over the lines in a file in reverse. The file must be
    open in 'rb' mode. Yields the lines unencoded (as bytes), including the
    newline character. Produces the same result as readlines, but reversed.
    If this is used to reverse the line in a file twice, the result is
    exactly the same.
    """
    head = b""
    f.seek(0, 2)
    t = f.tell()
    buffersize, maxbuffersize = 64, 4096
    while True:
        if t <= 0:
            break
        # Read next block
        buffersize = min(buffersize * 2, maxbuffersize)
        tprev = t
        t = max(0, t - buffersize)
        f.seek(t)
        lines = f.read(tprev - t).splitlines(True)
        # Align to line breaks
        if not lines[-1].endswith((b"\n", b"\r")):
            lines[-1] += head  # current tail is previous head
        elif head == b"\n" and lines[-1].endswith(b"\r"):
            lines[-1] += head  # Keep \r\n together
        elif head:
            lines.append(head)
        head = lines.pop(0)  # can be '\n' (ok)
        # Iterate over current block in reverse
        for line in reversed(lines):
            yield line
    if head:
        yield head

Most of the answers need to read the whole file before doing anything. This sample reads increasingly large samples from the end.

I only saw Murat Yükselen’s answer while writing this answer. It’s nearly the same, which I suppose is a good thing. The sample below also deals with \r and increases its buffersize at each step. I also have some unit tests to back this code up.

def readlines_reversed(f):
    """ Iterate over the lines in a file in reverse. The file must be
    open in 'rb' mode. Yields the lines unencoded (as bytes), including the
    newline character. Produces the same result as readlines, but reversed.
    If this is used to reverse the line in a file twice, the result is
    exactly the same.
    """
    head = b""
    f.seek(0, 2)
    t = f.tell()
    buffersize, maxbuffersize = 64, 4096
    while True:
        if t <= 0:
            break
        # Read next block
        buffersize = min(buffersize * 2, maxbuffersize)
        tprev = t
        t = max(0, t - buffersize)
        f.seek(t)
        lines = f.read(tprev - t).splitlines(True)
        # Align to line breaks
        if not lines[-1].endswith((b"\n", b"\r")):
            lines[-1] += head  # current tail is previous head
        elif head == b"\n" and lines[-1].endswith(b"\r"):
            lines[-1] += head  # Keep \r\n together
        elif head:
            lines.append(head)
        head = lines.pop(0)  # can be '\n' (ok)
        # Iterate over current block in reverse
        for line in reversed(lines):
            yield line
    if head:
        yield head

回答 16

逐行读取文件,然后以相反顺序将其添加到列表中。

这是代码示例:

reverse = []
with open("file.txt", "r") as file:
    for line in file:
        line = line.strip()
         reverse[0:0] = line

Read the file line by line and then add it on a list in reverse order.

Here is an example of code :

reverse = []
with open("file.txt", "r") as file:
    for line in file:
        line = line.strip()
         reverse[0:0] = line

回答 17

import sys
f = open(sys.argv[1] , 'r')
for line in f.readlines()[::-1]:
    print line
import sys
f = open(sys.argv[1] , 'r')
for line in f.readlines()[::-1]:
    print line

回答 18

def previous_line(self, opened_file):
        opened_file.seek(0, os.SEEK_END)
        position = opened_file.tell()
        buffer = bytearray()
        while position >= 0:
            opened_file.seek(position)
            position -= 1
            new_byte = opened_file.read(1)
            if new_byte == self.NEW_LINE:
                parsed_string = buffer.decode()
                yield parsed_string
                buffer = bytearray()
            elif new_byte == self.EMPTY_BYTE:
                continue
            else:
                new_byte_array = bytearray(new_byte)
                new_byte_array.extend(buffer)
                buffer = new_byte_array
        yield None

使用:

opened_file = open(filepath, "rb")
iterator = self.previous_line(opened_file)
line = next(iterator) #one step
close(opened_file)
def previous_line(self, opened_file):
        opened_file.seek(0, os.SEEK_END)
        position = opened_file.tell()
        buffer = bytearray()
        while position >= 0:
            opened_file.seek(position)
            position -= 1
            new_byte = opened_file.read(1)
            if new_byte == self.NEW_LINE:
                parsed_string = buffer.decode()
                yield parsed_string
                buffer = bytearray()
            elif new_byte == self.EMPTY_BYTE:
                continue
            else:
                new_byte_array = bytearray(new_byte)
                new_byte_array.extend(buffer)
                buffer = new_byte_array
        yield None

to use:

opened_file = open(filepath, "rb")
iterator = self.previous_line(opened_file)
line = next(iterator) #one step
close(opened_file)

回答 19

我不得不在一段时间前使用以下代码。它通过管道传递给外壳。恐怕我没有完整的脚本了。如果您使用的是unixish操作系统,则可以使用“ tac”,但是,例如在Mac OSX上,tac命令不起作用,请使用tail -r。以下代码段测试了您所使用的平台,并相应地调整了命令

# We need a command to reverse the line order of the file. On Linux this
# is 'tac', on OSX it is 'tail -r'
# 'tac' is not supported on osx, 'tail -r' is not supported on linux.

if sys.platform == "darwin":
    command += "|tail -r"
elif sys.platform == "linux2":
    command += "|tac"
else:
    raise EnvironmentError('Platform %s not supported' % sys.platform)

I had to do this some time ago and used the below code. It pipes to the shell. I am afraid i do not have the complete script anymore. If you are on a unixish operating system, you can use “tac”, however on e.g. Mac OSX tac command does not work, use tail -r. The below code snippet tests for which platform you’re on, and adjusts the command accordingly

# We need a command to reverse the line order of the file. On Linux this
# is 'tac', on OSX it is 'tail -r'
# 'tac' is not supported on osx, 'tail -r' is not supported on linux.

if sys.platform == "darwin":
    command += "|tail -r"
elif sys.platform == "linux2":
    command += "|tac"
else:
    raise EnvironmentError('Platform %s not supported' % sys.platform)

正确的方法来反转pandas.DataFrame?

问题:正确的方法来反转pandas.DataFrame?

这是我的代码:

import pandas as pd

data = pd.DataFrame({'Odd':[1,3,5,6,7,9], 'Even':[0,2,4,6,8,10]})

for i in reversed(data):
    print(data['Odd'], data['Even'])

当我运行此代码时,出现以下错误:

Traceback (most recent call last):
  File "C:\Python33\lib\site-packages\pandas\core\generic.py", line 665, in _get_item_cache
    return cache[item]
KeyError: 5

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\*****\Documents\******\********\****.py", line 5, in <module>
    for i in reversed(data):
  File "C:\Python33\lib\site-packages\pandas\core\frame.py", line 2003, in __getitem__
    return self._get_item_cache(key)
  File "C:\Python33\lib\site-packages\pandas\core\generic.py", line 667, in _get_item_cache
    values = self._data.get(item)
  File "C:\Python33\lib\site-packages\pandas\core\internals.py", line 1656, in get
    _, block = self._find_block(item)
  File "C:\Python33\lib\site-packages\pandas\core\internals.py", line 1936, in _find_block
    self._check_have(item)
  File "C:\Python33\lib\site-packages\pandas\core\internals.py", line 1943, in _check_have
    raise KeyError('no item named %s' % com.pprint_thing(item))
KeyError: 'no item named 5'

为什么会出现此错误?
我该如何解决?
正确的逆转方法是pandas.DataFrame什么?

Here is my code:

import pandas as pd

data = pd.DataFrame({'Odd':[1,3,5,6,7,9], 'Even':[0,2,4,6,8,10]})

for i in reversed(data):
    print(data['Odd'], data['Even'])

When I run this code, i get the following error:

Traceback (most recent call last):
  File "C:\Python33\lib\site-packages\pandas\core\generic.py", line 665, in _get_item_cache
    return cache[item]
KeyError: 5

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\*****\Documents\******\********\****.py", line 5, in <module>
    for i in reversed(data):
  File "C:\Python33\lib\site-packages\pandas\core\frame.py", line 2003, in __getitem__
    return self._get_item_cache(key)
  File "C:\Python33\lib\site-packages\pandas\core\generic.py", line 667, in _get_item_cache
    values = self._data.get(item)
  File "C:\Python33\lib\site-packages\pandas\core\internals.py", line 1656, in get
    _, block = self._find_block(item)
  File "C:\Python33\lib\site-packages\pandas\core\internals.py", line 1936, in _find_block
    self._check_have(item)
  File "C:\Python33\lib\site-packages\pandas\core\internals.py", line 1943, in _check_have
    raise KeyError('no item named %s' % com.pprint_thing(item))
KeyError: 'no item named 5'

Why am I getting this error?
How can I fix that?
What is the right way to reverse pandas.DataFrame?


回答 0

data.reindex(index=data.index[::-1])

或者简单地:

data.iloc[::-1]

将反转您的数据帧,如果您想使for循环从下到上,则可以执行以下操作:

for idx in reversed(data.index):
    print(idx, data.loc[idx, 'Even'], data.loc[idx, 'Odd'])

要么

for idx in reversed(data.index):
    print(idx, data.Even[idx], data.Odd[idx])

因为你得到一个错误reversed首先调用data.__len__()返回6,然后试图调用data[j - 1]用于jrange(6, 0, -1)和第一个电话会data[5]; 但在pandas数据框中data[5]表示第5列,没有第5列,因此它将引发异常。(请参阅文档

data.reindex(index=data.index[::-1])

or simply:

data.iloc[::-1]

will reverse your data frame, if you want to have a for loop which goes from down to up you may do:

for idx in reversed(data.index):
    print(idx, data.loc[idx, 'Even'], data.loc[idx, 'Odd'])

or

for idx in reversed(data.index):
    print(idx, data.Even[idx], data.Odd[idx])

You are getting an error because reversed first calls data.__len__() which returns 6. Then it tries to call data[j - 1] for j in range(6, 0, -1), and the first call would be data[5]; but in pandas dataframe data[5] means column 5, and there is no column 5 so it will throw an exception. ( see docs )


回答 1

您可以以更简单的方式反转行:

df[::-1]

You can reverse the rows in an even simpler way:

df[::-1]

回答 2

反转数据帧后,现有答案都不会重置索引。

为此,请执行以下操作:

 data[::-1].reset_index()

这是一个实用程序函数,它也按照@Tim的注释删除了旧的索引列:

def reset_my_index(df):
  res = df[::-1].reset_index(drop=True)
  return(res)

只需将数据框传递给函数

None of the existing answers resets the index after reversing the dataframe.

For this, do the following:

 data[::-1].reset_index()

Here’s a utility function that also removes the old index column, as per @Tim’s comment:

def reset_my_index(df):
  res = df[::-1].reset_index(drop=True)
  return(res)

Simply pass your dataframe into the function


回答 3

这有效:

    for i,r in data[::-1].iterrows():
        print(r['Odd'], r['Even'])

This works:

    for i,r in data[::-1].iterrows():
        print(r['Odd'], r['Even'])

Python列表按降序排序

问题:Python列表按降序排序

如何按降序对列表进行排序?

timestamp = [
    "2010-04-20 10:07:30",
    "2010-04-20 10:07:38",
    "2010-04-20 10:07:52",
    "2010-04-20 10:08:22",
    "2010-04-20 10:08:22",
    "2010-04-20 10:09:46",
    "2010-04-20 10:10:37",
    "2010-04-20 10:10:58",
    "2010-04-20 10:11:50",
    "2010-04-20 10:12:13",
    "2010-04-20 10:12:13",
    "2010-04-20 10:25:38"
]

How can I sort this list in descending order?

timestamp = [
    "2010-04-20 10:07:30",
    "2010-04-20 10:07:38",
    "2010-04-20 10:07:52",
    "2010-04-20 10:08:22",
    "2010-04-20 10:08:22",
    "2010-04-20 10:09:46",
    "2010-04-20 10:10:37",
    "2010-04-20 10:10:58",
    "2010-04-20 10:11:50",
    "2010-04-20 10:12:13",
    "2010-04-20 10:12:13",
    "2010-04-20 10:25:38"
]

回答 0

在一行中,使用lambda

timestamp.sort(key=lambda x: time.strptime(x, '%Y-%m-%d %H:%M:%S')[0:6], reverse=True)

将函数传递给list.sort

def foo(x):
    return time.strptime(x, '%Y-%m-%d %H:%M:%S')[0:6]

timestamp.sort(key=foo, reverse=True)

In one line, using a lambda:

timestamp.sort(key=lambda x: time.strptime(x, '%Y-%m-%d %H:%M:%S')[0:6], reverse=True)

Passing a function to list.sort:

def foo(x):
    return time.strptime(x, '%Y-%m-%d %H:%M:%S')[0:6]

timestamp.sort(key=foo, reverse=True)

回答 1

这将为您提供阵列的排序版本。

sorted(timestamp, reverse=True)

如果要就地排序:

timestamp.sort(reverse=True)

This will give you a sorted version of the array.

sorted(timestamp, reverse=True)

If you want to sort in-place:

timestamp.sort(reverse=True)

回答 2

您可以简单地做到这一点:

timestamp.sort(reverse=True)

You can simply do this:

timestamp.sort(reverse=True)

回答 3

由于您的列表已经按升序排列,因此我们可以简单地反转列表。

>>> timestamp.reverse()
>>> timestamp
['2010-04-20 10:25:38', 
'2010-04-20 10:12:13', 
'2010-04-20 10:12:13', 
'2010-04-20 10:11:50', 
'2010-04-20 10:10:58', 
'2010-04-20 10:10:37', 
'2010-04-20 10:09:46', 
'2010-04-20 10:08:22',
'2010-04-20 10:08:22', 
'2010-04-20 10:07:52', 
'2010-04-20 10:07:38', 
'2010-04-20 10:07:30']

Since your list is already in ascending order, we can simply reverse the list.

>>> timestamp.reverse()
>>> timestamp
['2010-04-20 10:25:38', 
'2010-04-20 10:12:13', 
'2010-04-20 10:12:13', 
'2010-04-20 10:11:50', 
'2010-04-20 10:10:58', 
'2010-04-20 10:10:37', 
'2010-04-20 10:09:46', 
'2010-04-20 10:08:22',
'2010-04-20 10:08:22', 
'2010-04-20 10:07:52', 
'2010-04-20 10:07:38', 
'2010-04-20 10:07:30']

回答 4

您简单的输入:

timestamp.sort()
timestamp=timestamp[::-1]

you simple type:

timestamp.sort()
timestamp=timestamp[::-1]

回答 5

这是另一种方式


timestamp.sort()
timestamp.reverse()
print(timestamp)

Here is another way


timestamp.sort()
timestamp.reverse()
print(timestamp)

在Python中以相反顺序遍历列表

问题:在Python中以相反顺序遍历列表

所以我可以从开始到len(collection)结束collection[0]

我还希望能够访问循环索引。

So I can start from len(collection) and end in collection[0].

I also want to be able to access the loop index.


回答 0

使用内置reversed()功能:

>>> a = ["foo", "bar", "baz"]
>>> for i in reversed(a):
...     print(i)
... 
baz
bar
foo

要同时访问原始索引,请enumerate()在列表上使用,然后将其传递给reversed()

>>> for i, e in reversed(list(enumerate(a))):
...     print(i, e)
... 
2 baz
1 bar
0 foo

由于enumerate()返回生成器并且生成器不能反转,因此需要将其转换为list第一个。

Use the built-in reversed() function:

>>> a = ["foo", "bar", "baz"]
>>> for i in reversed(a):
...     print(i)
... 
baz
bar
foo

To also access the original index, use enumerate() on your list before passing it to reversed():

>>> for i, e in reversed(list(enumerate(a))):
...     print(i, e)
... 
2 baz
1 bar
0 foo

Since enumerate() returns a generator and generators can’t be reversed, you need to convert it to a list first.


回答 1

你可以做:

for item in my_list[::-1]:
    print item

(或者您想要在for循环中执行的任何操作。)

[::-1]切片反转for循环在列表中(但不会实际修改列表的“永久”)。

You can do:

for item in my_list[::-1]:
    print item

(Or whatever you want to do in the for loop.)

The [::-1] slice reverses the list in the for loop (but won’t actually modify your list “permanently”).


回答 2

如果您需要循环索引,并且不想遍历整个列表两次,或者不想使用额外的内存,则可以编写一个生成器。

def reverse_enum(L):
   for index in reversed(xrange(len(L))):
      yield index, L[index]

L = ['foo', 'bar', 'bas']
for index, item in reverse_enum(L):
   print index, item

If you need the loop index, and don’t want to traverse the entire list twice, or use extra memory, I’d write a generator.

def reverse_enum(L):
   for index in reversed(xrange(len(L))):
      yield index, L[index]

L = ['foo', 'bar', 'bas']
for index, item in reverse_enum(L):
   print index, item

回答 3

可以这样完成:

对于范围(len(collect)-1,-1,-1)中的i:
    印刷品收藏[i]

    #为python 3打印(collection [i])。

因此,您的猜测非常接近:)有点尴尬,但这基本上是在说:从小于1开始len(collection),一直走到-1之前,以-1为步长。

Fyi,该help功能非常有用,因为它使您可以从Python控制台查看文档,例如:

help(range)

It can be done like this:

for i in range(len(collection)-1, -1, -1):
    print collection[i]

    # print(collection[i]) for python 3. +

So your guess was pretty close :) A little awkward but it’s basically saying: start with 1 less than len(collection), keep going until you get to just before -1, by steps of -1.

Fyi, the help function is very useful as it lets you view the docs for something from the Python console, eg:

help(range)


回答 4

reversed内置功能非常方便:

for item in reversed(sequence):

文档的反向解释它的局限性。

对于必须与索引一起反向遍历序列的情况(例如,对于更改序列长度的就地修改),我需要将此函数定义为我的codeutil模块:

import itertools
def reversed_enumerate(sequence):
    return itertools.izip(
        reversed(xrange(len(sequence))),
        reversed(sequence),
    )

这避免了创建序列的副本。显然,这些reversed限制仍然适用。

The reversed builtin function is handy:

for item in reversed(sequence):

The documentation for reversed explains its limitations.

For the cases where I have to walk a sequence in reverse along with the index (e.g. for in-place modifications changing the sequence length), I have this function defined an my codeutil module:

import itertools
def reversed_enumerate(sequence):
    return itertools.izip(
        reversed(xrange(len(sequence))),
        reversed(sequence),
    )

This one avoids creating a copy of the sequence. Obviously, the reversed limitations still apply.


回答 5

无需重新创建新列表,可以通过建立索引来实现:

>>> foo = ['1a','2b','3c','4d']
>>> for i in range(len(foo)):
...     print foo[-(i+1)]
...
4d
3c
2b
1a
>>>

要么

>>> length = len(foo)
>>> for i in range(length):
...     print foo[length-i-1]
...
4d
3c
2b
1a
>>>

How about without recreating a new list, you can do by indexing:

>>> foo = ['1a','2b','3c','4d']
>>> for i in range(len(foo)):
...     print foo[-(i+1)]
...
4d
3c
2b
1a
>>>

OR

>>> length = len(foo)
>>> for i in range(length):
...     print foo[length-i-1]
...
4d
3c
2b
1a
>>>

回答 6

>>> l = ["a","b","c","d"]
>>> l.reverse()
>>> l
['d', 'c', 'b', 'a']

要么

>>> print l[::-1]
['d', 'c', 'b', 'a']
>>> l = ["a","b","c","d"]
>>> l.reverse()
>>> l
['d', 'c', 'b', 'a']

OR

>>> print l[::-1]
['d', 'c', 'b', 'a']

回答 7

我喜欢单线生成器方法:

((i, sequence[i]) for i in reversed(xrange(len(sequence))))

I like the one-liner generator approach:

((i, sequence[i]) for i in reversed(xrange(len(sequence))))

回答 8

另外,您可以使用“范围”或“计数”功能。如下:

a = ["foo", "bar", "baz"]
for i in range(len(a)-1, -1, -1):
    print(i, a[i])

3 baz
2 bar
1 foo

您还可以按以下方式使用itertools中的“ count”:

a = ["foo", "bar", "baz"]
from itertools import count, takewhile

def larger_than_0(x):
    return x > 0

for x in takewhile(larger_than_0, count(3, -1)):
    print(x, a[x-1])

3 baz
2 bar
1 foo

Also, you could use either “range” or “count” functions. As follows:

a = ["foo", "bar", "baz"]
for i in range(len(a)-1, -1, -1):
    print(i, a[i])

3 baz
2 bar
1 foo

You could also use “count” from itertools as following:

a = ["foo", "bar", "baz"]
from itertools import count, takewhile

def larger_than_0(x):
    return x > 0

for x in takewhile(larger_than_0, count(3, -1)):
    print(x, a[x-1])

3 baz
2 bar
1 foo

回答 9

list.reverse()照常使用,然后进行迭代。

http://docs.python.org/tutorial/datastructures.html

Use list.reverse() and then iterate as you normally would.

http://docs.python.org/tutorial/datastructures.html


回答 10

没有导入的方法:

for i in range(1,len(arr)+1):
    print(arr[-i])

要么

for i in arr[::-1]:
    print(i)

An approach with no imports:

for i in range(1,len(arr)+1):
    print(arr[-i])

or

for i in arr[::-1]:
    print(i)

回答 11

def reverse(spam):
    k = []
    for i in spam:
        k.insert(0,i)
    return "".join(k)
def reverse(spam):
    k = []
    for i in spam:
        k.insert(0,i)
    return "".join(k)

回答 12

对于有什么价值的事情,您也可以这样做。很简单。

a = [1, 2, 3, 4, 5, 6, 7]
for x in xrange(len(a)):
    x += 1
    print a[-x]

for what ever it’s worth you can do it like this too. very simple.

a = [1, 2, 3, 4, 5, 6, 7]
for x in xrange(len(a)):
    x += 1
    print a[-x]

回答 13

reverse(enumerate(collection))在python 3中实现的一种表达方式:

zip(reversed(range(len(collection))), reversed(collection))

在python 2:

izip(reversed(xrange(len(collection))), reversed(collection))

我不确定为什么我们没有快捷方式,例如:

def reversed_enumerate(collection):
    return zip(reversed(range(len(collection))), reversed(collection))

还是为什么我们没有 reversed_range()

An expressive way to achieve reverse(enumerate(collection)) in python 3:

zip(reversed(range(len(collection))), reversed(collection))

in python 2:

izip(reversed(xrange(len(collection))), reversed(collection))

I’m not sure why we don’t have a shorthand for this, eg.:

def reversed_enumerate(collection):
    return zip(reversed(range(len(collection))), reversed(collection))

or why we don’t have reversed_range()


回答 14

如果您需要索引并且列表很小,那么最容易理解的方法是reversed(list(enumerate(your_list)))按照接受的答案进行操作。但这会创建列表的副本,因此,如果列表占用了很大一部分内存,则必须减去enumerate(reversed())从中返回的索引len()-1

如果您只需要执行一次:

a = ['b', 'd', 'c', 'a']

for index, value in enumerate(reversed(a)):
    index = len(a)-1 - index

    do_something(index, value)

或者,如果您需要多次执行此操作,则应使用生成器:

def enumerate_reversed(lyst):
    for index, value in enumerate(reversed(lyst)):
        index = len(lyst)-1 - index
        yield index, value

for index, value in enumerate_reversed(a):
    do_something(index, value)

If you need the index and your list is small, the most readable way is to do reversed(list(enumerate(your_list))) like the accepted answer says. But this creates a copy of your list, so if your list is taking up a large portion of your memory you’ll have to subtract the index returned by enumerate(reversed()) from len()-1.

If you just need to do it once:

a = ['b', 'd', 'c', 'a']

for index, value in enumerate(reversed(a)):
    index = len(a)-1 - index

    do_something(index, value)

or if you need to do this multiple times you should use a generator:

def enumerate_reversed(lyst):
    for index, value in enumerate(reversed(lyst)):
        index = len(lyst)-1 - index
        yield index, value

for index, value in enumerate_reversed(a):
    do_something(index, value)

回答 15

反向功能在这里很方便:

myArray = [1,2,3,4]
myArray.reverse()
for x in myArray:
    print x

the reverse function comes in handy here:

myArray = [1,2,3,4]
myArray.reverse()
for x in myArray:
    print x

回答 16

您还可以使用while循环:

i = len(collection)-1
while i>=0:
    value = collection[i]
    index = i
    i-=1

You can also use a while loop:

i = len(collection)-1
while i>=0:
    value = collection[i]
    index = i
    i-=1

回答 17

您可以在普通的for循环中使用负索引:

>>> collection = ["ham", "spam", "eggs", "baked beans"]
>>> for i in range(1, len(collection) + 1):
...     print(collection[-i])
... 
baked beans
eggs
spam
ham

要访问索引,就好像您正在遍历集合的反向副本一样,请使用i - 1

>>> for i in range(1, len(collection) + 1):
...     print(i-1, collection[-i])
... 
0 baked beans
1 eggs
2 spam
3 ham

要访问原始的非反向索引,请使用len(collection) - i

>>> for i in range(1, len(collection) + 1):
...     print(len(collection)-i, collection[-i])
... 
3 baked beans
2 eggs
1 spam
0 ham

You can use a negative index in an ordinary for loop:

>>> collection = ["ham", "spam", "eggs", "baked beans"]
>>> for i in range(1, len(collection) + 1):
...     print(collection[-i])
... 
baked beans
eggs
spam
ham

To access the index as though you were iterating forward over a reversed copy of the collection, use i - 1:

>>> for i in range(1, len(collection) + 1):
...     print(i-1, collection[-i])
... 
0 baked beans
1 eggs
2 spam
3 ham

To access the original, un-reversed index, use len(collection) - i:

>>> for i in range(1, len(collection) + 1):
...     print(len(collection)-i, collection[-i])
... 
3 baked beans
2 eggs
1 spam
0 ham

回答 18

如果您不介意索引为负,则可以执行以下操作:

>>> a = ["foo", "bar", "baz"]
>>> for i in range(len(a)):
...     print(~i, a[~i]))
-1 baz
-2 bar
-3 foo

If you don’t mind the index being negative, you can do:

>>> a = ["foo", "bar", "baz"]
>>> for i in range(len(a)):
...     print(~i, a[~i]))
-1 baz
-2 bar
-3 foo

回答 19

我认为最优雅的方法是转换enumeratereversed使用以下生成器

(-(ri+1), val) for ri, val in enumerate(reversed(foo))

生成enumerate迭代器的逆向

例:

foo = [1,2,3]
bar = [3,6,9]
[
    bar[i] - val
    for i, val in ((-(ri+1), val) for ri, val in enumerate(reversed(foo)))
]

结果:

[6, 4, 2]

I think the most elegant way is to transform enumerate and reversed using the following generator

(-(ri+1), val) for ri, val in enumerate(reversed(foo))

which generates a the reverse of the enumerate iterator

Example:

foo = [1,2,3]
bar = [3,6,9]
[
    bar[i] - val
    for i, val in ((-(ri+1), val) for ri, val in enumerate(reversed(foo)))
]

Result:

[6, 4, 2]

回答 20

其他答案是好的,但是如果您要作为 列表理解样式

collection = ['a','b','c']
[item for item in reversed( collection ) ]

The other answers are good, but if you want to do as List comprehension style

collection = ['a','b','c']
[item for item in reversed( collection ) ]

回答 21

要使用负索引,请执行以下操作:从-1开始,然后在每次迭代后退-1。

>>> a = ["foo", "bar", "baz"]
>>> for i in range(-1, -1*(len(a)+1), -1):
...     print i, a[i]
... 
-1 baz
-2 bar
-3 foo

To use negative indices: start at -1 and step back by -1 at each iteration.

>>> a = ["foo", "bar", "baz"]
>>> for i in range(-1, -1*(len(a)+1), -1):
...     print i, a[i]
... 
-1 baz
-2 bar
-3 foo

回答 22

一个简单的方法:

n = int(input())
arr = list(map(int, input().split()))

for i in reversed(range(0, n)):
    print("%d %d" %(i, arr[i]))

A simple way :

n = int(input())
arr = list(map(int, input().split()))

for i in reversed(range(0, n)):
    print("%d %d" %(i, arr[i]))

回答 23

input_list = ['foo','bar','baz']
for i in range(-1,-len(input_list)-1,-1)
    print(input_list[i])

我认为这也是一种简单的方法…从末尾读取并不断递减直到列表的长度,因为我们从不执行“ end”索引,因此也添加了-1

input_list = ['foo','bar','baz']
for i in range(-1,-len(input_list)-1,-1)
    print(input_list[i])

i think this one is also simple way to do it… read from end and keep decrementing till the length of list, since we never execute the “end” index hence added -1 also


回答 24

假设任务是在列表中找到满足某些条件的最后一个元素(即向后看时的第一个元素),我得到以下数字:

>>> min(timeit.repeat('for i in xrange(len(xs)-1,-1,-1):\n    if 128 == xs[i]: break', setup='xs, n = range(256), 0', repeat=8))
4.6937971115112305
>>> min(timeit.repeat('for i in reversed(xrange(0, len(xs))):\n    if 128 == xs[i]: break', setup='xs, n = range(256), 0', repeat=8))
4.809093952178955
>>> min(timeit.repeat('for i, x in enumerate(reversed(xs), 1):\n    if 128 == x: break', setup='xs, n = range(256), 0', repeat=8))
4.931743860244751
>>> min(timeit.repeat('for i, x in enumerate(xs[::-1]):\n    if 128 == x: break', setup='xs, n = range(256), 0', repeat=8))
5.548468112945557
>>> min(timeit.repeat('for i in xrange(len(xs), 0, -1):\n    if 128 == xs[i - 1]: break', setup='xs, n = range(256), 0', repeat=8))
6.286104917526245
>>> min(timeit.repeat('i = len(xs)\nwhile 0 < i:\n    i -= 1\n    if 128 == xs[i]: break', setup='xs, n = range(256), 0', repeat=8))
8.384078979492188

因此,最丑陋的选择xrange(len(xs)-1,-1,-1)是最快的。

Assuming task is to find last element that satisfies some condition in a list (i.e. first when looking backwards), I’m getting following numbers:

>>> min(timeit.repeat('for i in xrange(len(xs)-1,-1,-1):\n    if 128 == xs[i]: break', setup='xs, n = range(256), 0', repeat=8))
4.6937971115112305
>>> min(timeit.repeat('for i in reversed(xrange(0, len(xs))):\n    if 128 == xs[i]: break', setup='xs, n = range(256), 0', repeat=8))
4.809093952178955
>>> min(timeit.repeat('for i, x in enumerate(reversed(xs), 1):\n    if 128 == x: break', setup='xs, n = range(256), 0', repeat=8))
4.931743860244751
>>> min(timeit.repeat('for i, x in enumerate(xs[::-1]):\n    if 128 == x: break', setup='xs, n = range(256), 0', repeat=8))
5.548468112945557
>>> min(timeit.repeat('for i in xrange(len(xs), 0, -1):\n    if 128 == xs[i - 1]: break', setup='xs, n = range(256), 0', repeat=8))
6.286104917526245
>>> min(timeit.repeat('i = len(xs)\nwhile 0 < i:\n    i -= 1\n    if 128 == xs[i]: break', setup='xs, n = range(256), 0', repeat=8))
8.384078979492188

So, the ugliest option xrange(len(xs)-1,-1,-1) is the fastest.


回答 25

您可以使用生成器:

li = [1,2,3,4,5,6]
len_li = len(li)
gen = (len_li-1-i for i in range(len_li))

最后:

for i in gen:
    print(li[i])

希望对您有帮助。

you can use a generator:

li = [1,2,3,4,5,6]
len_li = len(li)
gen = (len_li-1-i for i in range(len_li))

finally:

for i in gen:
    print(li[i])

hope this help you.


反转/反转字典映射

问题:反转/反转字典映射

给定这样的字典:

my_map = {'a': 1, 'b': 2}

如何将这张地图倒置即可:

inv_map = {1: 'a', 2: 'b'}

Given a dictionary like so:

my_map = {'a': 1, 'b': 2}

How can one invert this map to get:

inv_map = {1: 'a', 2: 'b'}

回答 0

对于Python 2.7.x

inv_map = {v: k for k, v in my_map.iteritems()}

对于Python 3+:

inv_map = {v: k for k, v in my_map.items()}

For Python 2.7.x

inv_map = {v: k for k, v in my_map.iteritems()}

For Python 3+:

inv_map = {v: k for k, v in my_map.items()}

回答 1

假设字典中的值是唯一的:

dict((v, k) for k, v in my_map.iteritems())

Assuming that the values in the dict are unique:

dict((v, k) for k, v in my_map.iteritems())

回答 2

如果中的值my_map不是唯一的:

inv_map = {}
for k, v in my_map.iteritems():
    inv_map[v] = inv_map.get(v, [])
    inv_map[v].append(k)

If the values in my_map aren’t unique:

inv_map = {}
for k, v in my_map.iteritems():
    inv_map[v] = inv_map.get(v, [])
    inv_map[v].append(k)

回答 3

为此,同时保留映射类型(假设它是a dictdict子类):

def inverse_mapping(f):
    return f.__class__(map(reversed, f.items()))

To do this while preserving the type of your mapping (assuming that it is a dict or a dict subclass):

def inverse_mapping(f):
    return f.__class__(map(reversed, f.items()))

回答 4

尝试这个:

inv_map = dict(zip(my_map.values(), my_map.keys()))

(请注意,字典视图上的Python文档明确地保证了这一点,.keys()并且.values()其元素具有相同的顺序,这使得上述方法可以工作。)

或者:

inv_map = dict((my_map[k], k) for k in my_map)

或使用python 3.0的dict理解

inv_map = {my_map[k] : k for k in my_map}

Try this:

inv_map = dict(zip(my_map.values(), my_map.keys()))

(Note that the Python docs on dictionary views explicitly guarantee that .keys() and .values() have their elements in the same order, which allows the approach above to work.)

Alternatively:

inv_map = dict((my_map[k], k) for k in my_map)

or using python 3.0’s dict comprehensions

inv_map = {my_map[k] : k for k in my_map}

回答 5

另一种更实用的方法:

my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))

Another, more functional, way:

my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))

回答 6

这扩展了Robert的答案,适用于字典中的值不是唯一的情况。

class ReversibleDict(dict):

    def reversed(self):
        """
        Return a reversed dict, with common values in the original dict
        grouped into a list in the returned dict.

        Example:
        >>> d = ReversibleDict({'a': 3, 'c': 2, 'b': 2, 'e': 3, 'd': 1, 'f': 2})
        >>> d.reversed()
        {1: ['d'], 2: ['c', 'b', 'f'], 3: ['a', 'e']}
        """

        revdict = {}
        for k, v in self.iteritems():
            revdict.setdefault(v, []).append(k)
        return revdict

实现受到限制,因为您不能使用reversed两次并取回原始文件。因此它不是对称的。已通过Python 2.6测试。是一个我用来打印结果字典的用例。

如果您宁愿使用a而set不是a list,并且可能存在对此有意义的无序应用程序,而不是setdefault(v, []).append(k)use setdefault(v, set()).add(k)

This expands upon the answer by Robert, applying to when the values in the dict aren’t unique.

class ReversibleDict(dict):

    def reversed(self):
        """
        Return a reversed dict, with common values in the original dict
        grouped into a list in the returned dict.

        Example:
        >>> d = ReversibleDict({'a': 3, 'c': 2, 'b': 2, 'e': 3, 'd': 1, 'f': 2})
        >>> d.reversed()
        {1: ['d'], 2: ['c', 'b', 'f'], 3: ['a', 'e']}
        """

        revdict = {}
        for k, v in self.iteritems():
            revdict.setdefault(v, []).append(k)
        return revdict

The implementation is limited in that you cannot use reversed twice and get the original back. It is not symmetric as such. It is tested with Python 2.6. Here is a use case of how I am using to print the resultant dict.

If you’d rather use a set than a list, and there could exist unordered applications for which this makes sense, instead of setdefault(v, []).append(k), use setdefault(v, set()).add(k).


回答 7

我们还可以使用重复键反转字典defaultdict

from collections import Counter, defaultdict

def invert_dict(d):
    d_inv = defaultdict(list)
    for k, v in d.items():
        d_inv[v].append(k)
    return d_inv

text = 'aaa bbb ccc ddd aaa bbb ccc aaa' 
c = Counter(text.split()) # Counter({'aaa': 3, 'bbb': 2, 'ccc': 2, 'ddd': 1})
dict(invert_dict(c)) # {1: ['ddd'], 2: ['bbb', 'ccc'], 3: ['aaa']}  

这里

与使用的等效技术相比,此技术更简单,更快dict.setdefault()

We can also reverse a dictionary with duplicate keys using defaultdict:

from collections import Counter, defaultdict

def invert_dict(d):
    d_inv = defaultdict(list)
    for k, v in d.items():
        d_inv[v].append(k)
    return d_inv

text = 'aaa bbb ccc ddd aaa bbb ccc aaa' 
c = Counter(text.split()) # Counter({'aaa': 3, 'bbb': 2, 'ccc': 2, 'ddd': 1})
dict(invert_dict(c)) # {1: ['ddd'], 2: ['bbb', 'ccc'], 3: ['aaa']}  

See here:

This technique is simpler and faster than an equivalent technique using dict.setdefault().


回答 8

例如,您有以下字典:

dict = {'a': 'fire', 'b': 'ice', 'c': 'fire', 'd': 'water'}

而且您想以相反的形式获取它:

inverted_dict = {'fire': ['a', 'c'], 'ice': ['b'], 'water': ['d']}

第一个解决方案。要在字典中反转键值对,请使用for-loop方法:

# Use this code to invert dictionaries that have non-unique values

inverted_dict = dict()
for key, value in dict.items():
    inverted_dict.setdefault(value, list()).append(key)

第二解决方案。使用字典理解方法进行反演:

# Use this code to invert dictionaries that have unique values

inverted_dict = {value: key for key, value in dict.items()}

第三解。使用还原反转方法(取决于第二种解决方案):

# Use this code to invert dictionaries that have lists of values

dict = {value: key for key in inverted_dict for value in my_map[key]}

For instance, you have the following dictionary:

dict = {'a': 'fire', 'b': 'ice', 'c': 'fire', 'd': 'water'}

And you wanna get it in such an inverted form:

inverted_dict = {'fire': ['a', 'c'], 'ice': ['b'], 'water': ['d']}

First Solution. For inverting key-value pairs in your dictionary use a for-loop approach:

# Use this code to invert dictionaries that have non-unique values

inverted_dict = dict()
for key, value in dict.items():
    inverted_dict.setdefault(value, list()).append(key)

Second Solution. Use a dictionary comprehension approach for inversion:

# Use this code to invert dictionaries that have unique values

inverted_dict = {value: key for key, value in dict.items()}

Third Solution. Use reverting the inversion approach (relies on second solution):

# Use this code to invert dictionaries that have lists of values

dict = {value: key for key in inverted_dict for value in my_map[key]}

回答 9

列表和字典理解的结合。可以处理重复的钥匙

{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}

Combination of list and dictionary comprehension. Can handle duplicate keys

{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}

回答 10

如果值不是唯一的,并且您有点硬核:

inv_map = dict(
    (v, [k for (k, xx) in filter(lambda (key, value): value == v, my_map.items())]) 
    for v in set(my_map.values())
)

特别是对于大字典,请注意,此解决方案的效率远不及Python反向/反转映射的答案,因为它会循环items()多次。

If the values aren’t unique, and you’re a little hardcore:

inv_map = dict(
    (v, [k for (k, xx) in filter(lambda (key, value): value == v, my_map.items())]) 
    for v in set(my_map.values())
)

Especially for a large dict, note that this solution is far less efficient than the answer Python reverse / invert a mapping because it loops over items() multiple times.


回答 11

除了上面建议的其他功能之外,如果您喜欢lambdas:

invert = lambda mydict: {v:k for k, v in mydict.items()}

或者,您也可以采用这种方式:

invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )

In addition to the other functions suggested above, if you like lambdas:

invert = lambda mydict: {v:k for k, v in mydict.items()}

Or, you could do it this way too:

invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )

回答 12

我认为最好的方法是定义一个类。这是“对称字典”的实现:

class SymDict:
    def __init__(self):
        self.aToB = {}
        self.bToA = {}

    def assocAB(self, a, b):
        # Stores and returns a tuple (a,b) of overwritten bindings
        currB = None
        if a in self.aToB: currB = self.bToA[a]
        currA = None
        if b in self.bToA: currA = self.aToB[b]

        self.aToB[a] = b
        self.bToA[b] = a
        return (currA, currB)

    def lookupA(self, a):
        if a in self.aToB:
            return self.aToB[a]
        return None

    def lookupB(self, b):
        if b in self.bToA:
            return self.bToA[b]
        return None

如果需要,删除和迭代方法很容易实现。

与反转整个字典(这似乎是此页面上最流行的解决方案)相比,这种实现方式效率更高。更不用说,您可以根据需要在自己的SymDict中添加或删除值,并且逆字典将始终保持有效-如果仅对整个字典进行一次反向操作,则情况并非如此。

I think the best way to do this is to define a class. Here is an implementation of a “symmetric dictionary”:

class SymDict:
    def __init__(self):
        self.aToB = {}
        self.bToA = {}

    def assocAB(self, a, b):
        # Stores and returns a tuple (a,b) of overwritten bindings
        currB = None
        if a in self.aToB: currB = self.bToA[a]
        currA = None
        if b in self.bToA: currA = self.aToB[b]

        self.aToB[a] = b
        self.bToA[b] = a
        return (currA, currB)

    def lookupA(self, a):
        if a in self.aToB:
            return self.aToB[a]
        return None

    def lookupB(self, b):
        if b in self.bToA:
            return self.bToA[b]
        return None

Deletion and iteration methods are easy enough to implement if they’re needed.

This implementation is way more efficient than inverting an entire dictionary (which seems to be the most popular solution on this page). Not to mention, you can add or remove values from your SymDict as much as you want, and your inverse-dictionary will always stay valid — this isn’t true if you simply reverse the entire dictionary once.


回答 13

这处理非唯一值,并保留了唯一情况的大部分外观。

inv_map = {v:[k for k in my_map if my_map[k] == v] for v in my_map.itervalues()}

对于Python 3.x,请替换itervaluesvalues

This handles non-unique values and retains much of the look of the unique case.

inv_map = {v:[k for k in my_map if my_map[k] == v] for v in my_map.itervalues()}

For Python 3.x, replace itervalues with values.


回答 14

函数对于类型列表的值是对称的;执行reverse_dict(reverse_dict(dictionary))时,元组被覆盖到列表中

def reverse_dict(dictionary):
    reverse_dict = {}
    for key, value in dictionary.iteritems():
        if not isinstance(value, (list, tuple)):
            value = [value]
        for val in value:
            reverse_dict[val] = reverse_dict.get(val, [])
            reverse_dict[val].append(key)
    for key, value in reverse_dict.iteritems():
        if len(value) == 1:
            reverse_dict[key] = value[0]
    return reverse_dict

Function is symmetric for values of type list; Tuples are coverted to lists when performing reverse_dict(reverse_dict(dictionary))

def reverse_dict(dictionary):
    reverse_dict = {}
    for key, value in dictionary.iteritems():
        if not isinstance(value, (list, tuple)):
            value = [value]
        for val in value:
            reverse_dict[val] = reverse_dict.get(val, [])
            reverse_dict[val].append(key)
    for key, value in reverse_dict.iteritems():
        if len(value) == 1:
            reverse_dict[key] = value[0]
    return reverse_dict

回答 15

由于字典在字典中需要一个与值不同的唯一键,因此我们必须将反转的值附加到要包含在新的特定键中的排序列表中。

def r_maping(dictionary):
    List_z=[]
    Map= {}
    for z, x in dictionary.iteritems(): #iterate through the keys and values
        Map.setdefault(x,List_z).append(z) #Setdefault is the same as dict[key]=default."The method returns the key value available in the dictionary and if given key is not available then it will return provided default value. Afterward, we will append into the default list our new values for the specific key.
    return Map

Since dictionaries require one unique key within the dictionary unlike values, we have to append the reversed values into a list of sort to be included within the new specific keys.

def r_maping(dictionary):
    List_z=[]
    Map= {}
    for z, x in dictionary.iteritems(): #iterate through the keys and values
        Map.setdefault(x,List_z).append(z) #Setdefault is the same as dict[key]=default."The method returns the key value available in the dictionary and if given key is not available then it will return provided default value. Afterward, we will append into the default list our new values for the specific key.
    return Map

回答 16

非双射映射的快速功能解决方案(值不是唯一的):

from itertools import imap, groupby

def fst(s):
    return s[0]

def snd(s):
    return s[1]

def inverseDict(d):
    """
    input d: a -> b
    output : b -> set(a)
    """
    return {
        v : set(imap(fst, kv_iter))
        for (v, kv_iter) in groupby(
            sorted(d.iteritems(),
                   key=snd),
            key=snd
        )
    }

从理论上讲,这应该比在命令式解决方案中一个接一个地添加到集合(或追加到列表中)要快。

不幸的是,值必须是可排序的,groupby要求排序。

Fast functional solution for non-bijective maps (values not unique):

from itertools import imap, groupby

def fst(s):
    return s[0]

def snd(s):
    return s[1]

def inverseDict(d):
    """
    input d: a -> b
    output : b -> set(a)
    """
    return {
        v : set(imap(fst, kv_iter))
        for (v, kv_iter) in groupby(
            sorted(d.iteritems(),
                   key=snd),
            key=snd
        )
    }

In theory this should be faster than adding to the set (or appending to the list) one by one like in the imperative solution.

Unfortunately the values have to be sortable, the sorting is required by groupby.


回答 17

尝试使用python 2.7 / 3.x

inv_map={};
for i in my_map:
    inv_map[my_map[i]]=i    
print inv_map

Try this for python 2.7/3.x

inv_map={};
for i in my_map:
    inv_map[my_map[i]]=i    
print inv_map

回答 18

我会在python 2中那样做。

inv_map = {my_map[x] : x for x in my_map}

I would do it that way in python 2.

inv_map = {my_map[x] : x for x in my_map}

回答 19

def invertDictionary(d):
    myDict = {}
  for i in d:
     value = d.get(i)
     myDict.setdefault(value,[]).append(i)   
 return myDict
 print invertDictionary({'a':1, 'b':2, 'c':3 , 'd' : 1})

这将提供以下输出:{1:[‘a’,’d’],2:[‘b’],3:[‘c’]}

def invertDictionary(d):
    myDict = {}
  for i in d:
     value = d.get(i)
     myDict.setdefault(value,[]).append(i)   
 return myDict
 print invertDictionary({'a':1, 'b':2, 'c':3 , 'd' : 1})

This will provide output as : {1: [‘a’, ‘d’], 2: [‘b’], 3: [‘c’]}


回答 20

  def reverse_dictionary(input_dict):
      out = {}
      for v in input_dict.values():  
          for value in v:
              if value not in out:
                  out[value.lower()] = []

      for i in input_dict:
          for j in out:
              if j in map (lambda x : x.lower(),input_dict[i]):
                  out[j].append(i.lower())
                  out[j].sort()
      return out

这段代码是这样的:

r = reverse_dictionary({'Accurate': ['exact', 'precise'], 'exact': ['precise'], 'astute': ['Smart', 'clever'], 'smart': ['clever', 'bright', 'talented']})

print(r)

{'precise': ['accurate', 'exact'], 'clever': ['astute', 'smart'], 'talented': ['smart'], 'bright': ['smart'], 'exact': ['accurate'], 'smart': ['astute']}
  def reverse_dictionary(input_dict):
      out = {}
      for v in input_dict.values():  
          for value in v:
              if value not in out:
                  out[value.lower()] = []

      for i in input_dict:
          for j in out:
              if j in map (lambda x : x.lower(),input_dict[i]):
                  out[j].append(i.lower())
                  out[j].sort()
      return out

this code do like this:

r = reverse_dictionary({'Accurate': ['exact', 'precise'], 'exact': ['precise'], 'astute': ['Smart', 'clever'], 'smart': ['clever', 'bright', 'talented']})

print(r)

{'precise': ['accurate', 'exact'], 'clever': ['astute', 'smart'], 'talented': ['smart'], 'bright': ['smart'], 'exact': ['accurate'], 'smart': ['astute']}

回答 21

没有什么完全不同,只是Cookbook重写了一些食谱。还通过保留setdefault方法进行了优化,而不是每次通过实例进行优化:

def inverse(mapping):
    '''
    A function to inverse mapping, collecting keys with simillar values
    in list. Careful to retain original type and to be fast.
    >> d = dict(a=1, b=2, c=1, d=3, e=2, f=1, g=5, h=2)
    >> inverse(d)
    {1: ['f', 'c', 'a'], 2: ['h', 'b', 'e'], 3: ['d'], 5: ['g']}
    '''
    res = {}
    setdef = res.setdefault
    for key, value in mapping.items():
        setdef(value, []).append(key)
    return res if mapping.__class__==dict else mapping.__class__(res)

设计下CPython的3.X中运行,为2.X替换mapping.items()mapping.iteritems()

在我的机器上,运行速度比这里的其他示例更快

Not something completely different, just a bit rewritten recipe from Cookbook. It’s futhermore optimized by retaining setdefault method, instead of each time getting it through the instance:

def inverse(mapping):
    '''
    A function to inverse mapping, collecting keys with simillar values
    in list. Careful to retain original type and to be fast.
    >> d = dict(a=1, b=2, c=1, d=3, e=2, f=1, g=5, h=2)
    >> inverse(d)
    {1: ['f', 'c', 'a'], 2: ['h', 'b', 'e'], 3: ['d'], 5: ['g']}
    '''
    res = {}
    setdef = res.setdefault
    for key, value in mapping.items():
        setdef(value, []).append(key)
    return res if mapping.__class__==dict else mapping.__class__(res)

Designed to be run under CPython 3.x, for 2.x replace mapping.items() with mapping.iteritems()

On my machine runs a bit faster, than other examples here


回答 22

我在循环“ for”和方法“ .get()”的帮助下编写了此代码,并将字典的名称“ map”更改为“ map1”,因为“ map”是一个函数。

def dict_invert(map1):
    inv_map = {} # new dictionary
    for key in map1.keys():
        inv_map[map1.get(key)] = key
    return inv_map

I wrote this with the help of cycle ‘for’ and method ‘.get()’ and I changed the name ‘map’ of the dictionary to ‘map1’ because ‘map’ is a function.

def dict_invert(map1):
    inv_map = {} # new dictionary
    for key in map1.keys():
        inv_map[map1.get(key)] = key
    return inv_map

回答 23

如果值不是唯一的,并且可能是哈希(一维):

for k, v in myDict.items():
    if len(v) > 1:
        for item in v:
            invDict[item] = invDict.get(item, [])
            invDict[item].append(k)
    else:
        invDict[v] = invDict.get(v, [])
        invDict[v].append(k)

如果需要更深层次地进行递归,则只需一个维度即可:

def digList(lst):
    temp = []
    for item in lst:
        if type(item) is list:
            temp.append(digList(item))
        else:
            temp.append(item)
    return set(temp)

for k, v in myDict.items():
    if type(v) is list:
        items = digList(v)
        for item in items:
            invDict[item] = invDict.get(item, [])
            invDict[item].append(k)
    else:
        invDict[v] = invDict.get(v, [])
        invDict[v].append(k)

If values aren’t unique AND may be a hash (one dimension):

for k, v in myDict.items():
    if len(v) > 1:
        for item in v:
            invDict[item] = invDict.get(item, [])
            invDict[item].append(k)
    else:
        invDict[v] = invDict.get(v, [])
        invDict[v].append(k)

And with a recursion if you need to dig deeper then just one dimension:

def digList(lst):
    temp = []
    for item in lst:
        if type(item) is list:
            temp.append(digList(item))
        else:
            temp.append(item)
    return set(temp)

for k, v in myDict.items():
    if type(v) is list:
        items = digList(v)
        for item in items:
            invDict[item] = invDict.get(item, [])
            invDict[item].append(k)
    else:
        invDict[v] = invDict.get(v, [])
        invDict[v].append(k)