将整数列表转换为一个数字?

84

我有一个整数列表,想将它们转换为一个数,例如:

numList = [1, 2, 3]
num = magic(numList)

print num, type(num)
>>> 123, <type 'int'>

如何最好地实现magic函数?

编辑
我找到了这个,但似乎肯定有更好的方法。


看來你是假定了十進制。是嗎? - S.Lott
int(''.join([str(n) for n in numList])) - Ali Waqas
20个回答

184
# Over-explaining a bit:
def magic(numList):         # [1,2,3]
    s = map(str, numList)   # ['1','2','3']
    s = ''.join(s)          # '123'
    s = int(s)              # 123
    return s


# How I'd probably write it:
def magic(numList):
    s = ''.join(map(str, numList))
    return int(s)


# As a one-liner  
num = int(''.join(map(str,numList)))


# Functionally:
s = reduce(lambda x,y: x+str(y), numList, '')
num = int(s)


# Using some oft-forgotten built-ins:
s = filter(str.isdigit, repr(numList))
num = int(s)

我猜你的“# How I'd probably write it:”里有一个错误,应该是''.join(map(str, numList))?另外,对于你的“聪明”的选项,你需要将结果转换为int类型。 - John Fouhy
3
TokenMacGuy: 你是指这个吗?- http://www.artima.com/weblogs/viewpost.jsp?thread=98196 map、reduce、filter和lambda最初都要在3k中使用。 - James Brady
对于前两种方法,我给一个赞,使用“函数式”方式似乎有点傻,而使用过滤器/str.isdigit的方式则像是一种可怕的hack。 - dbr
2
函数式方法的小改进: reduce(lambda x,y: 10*x+y, numList) - Dave Radcliffe
1
@DaveRadcliffe:对于小的len(numList),[reduce(lambda n,d:10*n+d,.. 比 'int(''.join(map(..` 更快。](https://gist.github.com/zed/ac0f5df365dffdb94f97476a89d38c8f#file-summary-org) - jfs
显示剩余3条评论

56

有两种解决方案:

>>> nums = [1, 2, 3]
>>> magic = lambda nums: int(''.join(str(i) for i in nums)) # Generator exp.
>>> magic(nums)
123
>>> magic = lambda nums: sum(digit * 10 ** (len(nums) - 1 - i) # Summation
...     for i, digit in enumerate(nums))
>>> magic(nums)
123

在我的计算机上,采用 map 方式的解决方案确实更好 - 当你处理可能是大数字时,绝对不应该使用 sum

Timeit Comparison

import collections
import random
import timeit

import matplotlib.pyplot as pyplot

MICROSECONDS_PER_SECOND = 1E6
FUNS = []
def test_fun(fun):
    FUNS.append(fun)
    return fun

@test_fun
def with_map(nums):
    return int(''.join(map(str, nums)))

@test_fun
def with_interpolation(nums):
    return int(''.join('%d' % num for num in nums))

@test_fun
def with_genexp(nums):
    return int(''.join(str(num) for num in nums))

@test_fun
def with_sum(nums):
    return sum(digit * 10 ** (len(nums) - 1 - i)
        for i, digit in enumerate(nums))

@test_fun
def with_reduce(nums):
    return int(reduce(lambda x, y: x + str(y), nums, ''))

@test_fun
def with_builtins(nums):
    return int(filter(str.isdigit, repr(nums)))

@test_fun
def with_accumulator(nums):
    tot = 0
    for num in nums:
        tot *= 10
        tot += num
    return tot

def time_test(digit_count, test_count=10000):
    """
    :return: Map from func name to (normalized) microseconds per pass.
    """
    print 'Digit count:', digit_count
    nums = [random.randrange(1, 10) for i in xrange(digit_count)]
    stmt = 'to_int(%r)' % nums
    result_by_method = {}
    for fun in FUNS:
        setup = 'from %s import %s as to_int' % (__name__, fun.func_name)
        t = timeit.Timer(stmt, setup)
        per_pass = t.timeit(number=test_count) / test_count
        per_pass *= MICROSECONDS_PER_SECOND
        print '%20s: %.2f usec/pass' % (fun.func_name, per_pass)
        result_by_method[fun.func_name] = per_pass
    return result_by_method

if __name__ == '__main__':
    pass_times_by_method = collections.defaultdict(list)
    assert_results = [fun([1, 2, 3]) for fun in FUNS]
    assert all(result == 123 for result in assert_results)
    digit_counts = range(1, 100, 2)
    for digit_count in digit_counts:
        for method, result in time_test(digit_count).iteritems():
            pass_times_by_method[method].append(result)
    for method, pass_times in pass_times_by_method.iteritems():
        pyplot.plot(digit_counts, pass_times, label=method)
    pyplot.legend(loc='upper left')
    pyplot.xlabel('Number of Digits')
    pyplot.ylabel('Microseconds')
    pyplot.show()

1
没问题,但记住你应该使用最易读的方式,除非你发现它是瓶颈。我只是喜欢计时而已。;-) - cdleary
我已经测量了上述函数的性能。结果略有不同,例如对于小的digit_count,使用with_accumulator()更快。请参阅https://dev59.com/znRB5IYBdhLWcg3w26x3#493944。 - jfs
我可以确认,在我的机器 M1 + python3 上,with_accumulator 看起来更好。 - Andrei Sura

11
def magic(number):
    return int(''.join(str(i) for i in number))

挑剔一点——您可以删除 [ ] 并将 str 作为生成表达式。int(''.join(str(i) for i in number))- 这样会快两个字节! - dbr

7

为了完整起见,这里有一个使用 print() 的变体(适用于 Python 2.6-3.x):

from __future__ import print_function
try: from cStringIO import StringIO
except ImportError:
     from io import StringIO

def to_int(nums, _s = StringIO()):
    print(*nums, sep='', end='', file=_s)
    s = _s.getvalue()
    _s.truncate(0)
    return int(s)

不同方案的时间性能

我测试了@cdleary的函数的性能。结果略有不同。

每个函数都使用以下生成的输入列表进行测试:

def randrange1_10(digit_count): # same as @cdleary
    return [random.randrange(1, 10) for i in xrange(digit_count)]

您可以通过命令行参数 --sequence-creator=yourmodule.yourfunction 提供自己的函数(参见下文)。
对于列表中给定数量(len(nums) == digit_count)的整数,最快的函数为:
  • len(nums) in 1..30

    def _accumulator(nums):
        tot = 0
        for num in nums:
            tot *= 10
            tot += num
        return tot
    
  • len(nums) in 30..1000

    def _map(nums):
        return int(''.join(map(str, nums)))
    
    def _imap(nums):
        return int(''.join(imap(str, nums)))
    

Figure: N = 1000

|------------------------------+-------------------|
| Fitting polynom              | Function          |
|------------------------------+-------------------|
| 1.00  log2(N)   +  1.25e-015 | N                 |
| 2.00  log2(N)   +  5.31e-018 | N*N               |
| 1.19  log2(N)   +      1.116 | N*log2(N)         |
| 1.37  log2(N)   +      2.232 | N*log2(N)*log2(N) |
|------------------------------+-------------------|
| 1.21  log2(N)   +      0.063 | _interpolation    |
| 1.24  log2(N)   -      0.610 | _genexp           |
| 1.25  log2(N)   -      0.968 | _imap             |
| 1.30  log2(N)   -      1.917 | _map              |

图:N = 1000_000

要绘制第一张图,请下载cdleary.pymake-figures.py并运行(必须安装numpymatplotlib才能绘制):

$ python cdleary.py 

或者
$ python make-figures.py --sort-function=cdleary._map \
> --sort-function=cdleary._imap \
> --sort-function=cdleary._interpolation \
> --sort-function=cdleary._genexp --sort-function=cdleary._sum \
> --sort-function=cdleary._reduce --sort-function=cdleary._builtins \
> --sort-function=cdleary._accumulator \
> --sequence-creator=cdleary.randrange1_10 --maxn=1000 

这是一种奇怪的Python 2.6/3.0写法... print(''.join(str(x) for x in [1,2,3,4,5])) 可以在Python 2.5、2.6、3.x等版本中使用,可能还有更多... - dbr
@dbr:目的是使用print函数。这不是推荐的方法,这就是为什么我写了“为了完整性”。 - jfs

6
def magic(numbers):
    return int(''.join([ "%d"%x for x in numbers]))

4

一行代码无需进行str的转换

def magic(num):
    return sum(e * 10**i for i, e in enumerate(num[::-1]))

4
伪代码:
int magic(list nums)
{
  int tot = 0;
while (!nums.isEmpty()) { int digit = nums.takeFirst(); tot *= 10; tot += digit; }
return tot; }

该函数将一个数字列表转换为整数。它通过遍历列表中的每个数字,并将其添加到累加器中来实现。最终,累加器中的值将是列表中所有数字的组合。

我认为你错过了他寻找的 Python 解决方案这一部分 :P - Dana
没问题 - 安德鲁的解决方案在转换为Python后实际上是最快的之一。我给他点赞! - cdleary
如果列表大小小于30,则这是最快的解决方案(@cdleary在Python中的实现)。https://dev59.com/znRB5IYBdhLWcg3w26x3#493944 - jfs

4

如果列表只包含整数:

reduce(lambda x,y: x*10+y, list)

对于一般观察者来说可能不太明显,因此警告一下:这仅适用于十进制。如果列表中的项大于9,则似乎也无法正常工作。例如,[1, 250] 应该等于1250,但答案是260。这个答案可以正常工作:https://dev59.com/znRB5IYBdhLWcg3w26x3#37451273 - Anthony

3

只要列表中的每个元素都只是一个数字,该方法在2.x版本中就可以使用。但你不应该真正使用它,因为它很糟糕。

>>> magic = lambda l:int(`l`[1::3])
>>> magic([3,1,3,3,7])
31337

3
使用生成器表达式:
def magic(numbers):
    digits = ''.join(str(n) for n in numbers)
    return int(digits)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接