初学者想知道他的代码是否是'Pythonic'

时间:2008-09-25 17:52:04

标签: python

这是我用python写的第一件事。我来自Java背景。我不想只学习如何用Python语法编写java代码。我想学习如何用pythonic范例编程。

你们可以评论我如何使下面的代码更加pythonic吗?

from math import sqrt

# recursively computes the factors of a number
def factors(num):
    factorList = []
    numroot = int(sqrt(num)) + 1
    numleft = num
    # brute force divide the number until you find a factor
    for i in range(2, numroot):
        if num % i == 0:
            # if we found a factor, add it to the list and compute the remainder
            factorList.append(i)
            numleft = num / i
            break
    # if we didn't find a factor, get out of here!
    if numleft == num: 
        factorList.append(num)
        return factorList
    # now recursively find the rest of the factors
    restFactors = factors(numleft)
    factorList.extend(restFactors)

    return factorList

# grabs  all of the twos in the list and puts them into 2 ^ x form
def transformFactorList(factorList):
    num2s = 0
    # remove all twos, counting them as we go
    while 2 in factorList:
        factorList.remove(2)
        num2s += 1
    # simply return the list with the 2's back in the right spot
    if num2s == 0: return factorList
    if num2s == 1:
        factorList.insert(0, 2)
        return factorList
    factorList.insert(0, '2 ^ ' + str(num2s))
    return factorList

print transformFactorList(factors(#some number))

12 个答案:

答案 0 :(得分:22)

大卫·古德尔(David Goodger)有一本名为“像Python一样的代码”here的优秀入门书。该文中的一些内容重新命名(引用):

  • joined_lower了解函数,方法, 属性

  • joined_lower或ALL_CAPS for 常数

  • StudlyCaps用于课程

  • camelCase只是为了顺从 已有的惯例

答案 1 :(得分:21)

只需使用'import math'和'math.sqrt()'代替'from math import sqrt'和'sqrt()';你只是导入'sqrt'就不会赢得任何东西,并且代码很快会因为太多的导入而变得难以处理。此外,当你大量使用from-import时,reload()和mock for out等测试会更快地破解。

divmod()函数是执行除法和模数的便捷方式。您可以使用for / else而不是对numleft进行单独检查。您的因子功能是发电机的自然候选者。另一个答案中已经提到了xrange()。这就是以这种方式完成的:

import math

# recursively computes the factors of a number as a generator
def factors(num):
    numroot = int(math.sqrt(num)) + 1
    # brute force divide the number until you find a factor
    for i in xrange(2, numroot):
        divider, remainder = divmod(num, i)
        if not remainder:
            # if we found a factor, add it to the list and compute the
            # remainder
            yield i
            break
    else:
    # if we didn't find a factor, get out of here!
        yield num
        return
    # now recursively find the rest of the factors
    for factor in factors(divider):
        yield factor

使用生成器意味着您只能迭代结果一次;如果你只是想要一个列表(就像你在translateFactorsList中那样),你将不得不将调用包装在list()中的factors()中。

答案 2 :(得分:17)

您可能想要看的另一件事是文档字符串。例如,此函数的注释:

# recursively computes the factors of a number
def factors(num):

可以转换成这个:

def factors(num):
    """ recursively computes the factors of a number"""

以这种方式实现这一点并不是100%必要,但如果你开始使用pydoc这样的东西,这是一个很好的习惯。

你也可以这样做:

docstring.py

"""This is a docstring"""

在命令行:

>>> import docstring
>>> help(docstring)

结果:

Help on module docstring:

NAME
    docstring - This is a docstring

FILE
    /Users/jason/docstring.py

答案 3 :(得分:8)

一些评论:

  1. 我会将range()替换为xrange();当你调用range()时,它会一次性分配整个范围,而当你遍历xrange()时,它会一次返回一个结果,从而节省内存。
  2. 不要将条件之后的表达式放在同一行(if num2s -- 0: return factorList)上。它使得一眼就能看到它正在做什么变得更加困难(这是一个障碍)。
  3. 不要害怕使用模块。 [sympy][1]模块已经有了计算因子的代码,这可以通过消除大部分代码来简化代码。
  4. Python的字符串格式简单有效。
  5. 例如:

    factorList.insert(0, '2 ^ ' + str(num2s))
    

    可以更改为

    factorlist.insert(0, '2 ^ %s' % num2s)
    

    总而言之,我发现你的代码并不是广泛的非pythonic。只要确保你想使用floor division,因为这是默认情况下整数值会发生的事情。否则,您需要修复除法运算符:

    from __future__ import division
    

    有时令人沮丧的语言警告。

答案 4 :(得分:4)

from itertools import takewhile

def transform_factor_list(factor_list):
    num_2s = len(list(takewhile(lambda e: e == 2, factor_list)))
    if num_2s > 1:
        factor_list[:num_2s] = ["2 ^ %i" % (num_2s, )]
    return factor_list

这就是我从第二个功能中得到的结果。

大多数pythonic变化:

  • PEP-8兼容命名
  • 切片(并分配给切片)
  • 迭代
  • 字符串格式化

该函数假定输入是有序的,这可以通过因子来实现。

编辑:删除了某些列表的特殊情况,这种方式更紧凑

答案 5 :(得分:4)

根据克里斯的回答,稍加简化:

  • 代替外部而
  • 内部同时保留重复使用相同除数的能力
  • 使用itertools.groupby简化compress()by
  • 修复了tostring()
  • 中的一个小错误

HTH:

import itertools

def factorize(n):
    # ideally an iterator of prime numbers
    # this'll work though
    divisors = itertools.count(2)

    for divisor in divisors:
        # This condition is very clever!
        # Note that `n` is decreasing, while `divisor` is increasing.
        # And we know that `n` is not divisible by anything smaller,
        # so this stops as soon as the remaining `n` is obviously prime.
        if divisor**2 > n:
            yield n
            break

        while n % divisor == 0:
            yield divisor
            n //= divisor

def compress(factors):
    for (factor, copies) in itertools.groupby(factors):
        # The second object yielded by groupby is a generator of equal factors.
        # Using list() to count its length.
        power = len(list(copies))
        yield (factor, power)

def tostring(compressed):
    return ' * '.join("%d**%d" % (factor, power) for (factor, power) in compressed)

# test
assert tostring(compress(factorize(12))) == '2**2 * 3**1'

答案 6 :(得分:3)

不要害怕列表理解。从Java切换到Python并发现它们是一个美好的一天。

对于因子函数,可能是这样的:

def factors(num):
    return [i for i in xrange(1, num+1) if num % i == 0]

可能不是最好的代码,但它简短易懂。

祝你好运,它是一种很棒的语言。

答案 7 :(得分:3)

这就是我这样做的方式......

import itertools
import collections

def factorize(n):
    # ideally an iterator of prime numbers
    # this'll work though
    divisors = itertools.count(2)

    divisor = divisors.next()
    while True:
        if divisor**2 > n:
            yield n
            break

        a,b = divmod(n, divisor)

        if b == 0:
            yield divisor
            n = a
        else:
            divisor = divisors.next()

def compress(factors):
    summands = collections.defaultdict(lambda: 0)

    for factor in factors:
        summands[factor] += 1

    return [(base, summands[base]) for base in sorted(summands)]

def tostring(compressed):
    return ' * '.join("%d**%d" % factor for factor in compressed)

答案 8 :(得分:2)

以下是对我的反应:

def transformFactorList(factorList):
    oldsize = len(factorList)
    factorList = [f for f in factorList if f != 2]
    num2s = oldsize - len(factorList)
    if num2s == 0:
        return []
    if num2s == 1:
        return [2]+factorList
     return ['2 ^ %s' % num2s] + [factorList]

表单[f for f in factorList if f != 2]称为列表理解。

答案 9 :(得分:2)

由于这篇文章似乎被Casey(lol)复活了,我将加上2美分。

浏览 PEP-8 中的所有内容。当我遇到代码格式化问题时,它帮助了我。

答案 10 :(得分:2)

我会使用列表理解来解决这两个问题:

def transformFactorList(factorList):
    twos = [x for x in factorList if x == 2]
    rest = [x for x in factorList if x != 2]
    rest.insert(0, "2 ^ %d" % len(twos))
    return rest

请注意,这会为您提供2^02^1,而您的代码却没有。你正在做什么两个似乎是任意的(有时你得到一个字符串,有时一个数字,有时没有),所以我认为这将没事。如果您愿意,可以轻松更改:

def transformFactorList(factorList):
    twos = [x for x in factorList if x == 2]
    rest = [x for x in factorList if x != 2]
    if twos:
        rest.insert(0, 2 if len(twos)==1 else "2 ^ %d" % len(twos))
    return rest

答案 11 :(得分:1)

使用递归(在没有必要的地方)不是pythonic。 Python没有尾递归消除, flat优于嵌套

如有疑问,请尝试import this

更新:按照热门请求,这里进行迭代分解(叹气):

"""returns an iterator of tuples (factor, power) such that 
reduce(operator.mul, (factor**power for factor, power in factors(n))) == n """
def factors(n):
    i = 2
    while n > 1:
        p = 0
        while n > 1 and n % i == 0:
            p += 1
            n /= i
        if p:
            yield (i, p)
        i += 1