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

时间:2020-03-06 14:44:14  来源:igfitidea点击:

这确实是我用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))

解决方案

这就是我的意思:

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!= 2时为factorList中f的f",称为列表理解。

一些评论:

  • 我用xrange()代替range();当我们调用range()时,它将一次分配整个范围,而当我们遍历xrange()时,它将一次返回每个结果,从而节省了内存。
  • 不要将条件之后的表达式放在同一行(如果num2s-0:返回factorList`)。一眼就很难看清它在做什么(这是一个障碍)。
  • 不要害怕使用模块。 [sympy] [1]模块已经具有计算因子的代码,这可以通过消除大部分代码来简化代码。
  • Python的字符串格式既简单又有效。

例如:

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

可以更改为

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

总而言之,我没有发现代码完全是非Python的。只需确保要使用下限分割即可,因为默认情况下,整数值往往会发生这种情况。否则,我们将需要修正除法运算符:

from __future__ import division

语言有时令人沮丧。

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兼容命名
  • 切片(并分配给切片)
  • 迭代器
  • 字符串格式

该函数假定输入是有序的,这由因素来满足。

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

只需使用" import math"和" math.sqrt()",而不是" from math import sqrt"和" sqrt()"即可;仅仅导入'sqrt'并不能赢得任何好处,并且过多的导入代码很快会使代码变得笨拙。另外,当我们大量使用from-import时,诸如reload()和模拟测试之类的操作会更快地中断。

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()中。

大卫·古德格(David Goodger)在这里有个很棒的入门书,叫做《像Pythonista的代码》。该文本中的几件事重新命名(引用):

  • " joined_lower",用于函数,方法,属性
  • joined_lower或者ALL_CAPS常量
  • 类的StudlyCaps
  • camelCase仅符合先前的约定

我们可能想看的另一件事是文档字符串。例如,此功能的注释:

# 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

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

对于因子函数,也许是这样的:

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

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

使用Python祝我们好运,这是一门很棒的语言。

由于该帖子似乎由Casey(lol)复活,因此我将加2美分。

仔细阅读PEP-8中的所有内容。当我遇到代码格式问题时,它可以极大地帮助我。

这就是我要这样做的方式。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

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)

根据克里斯的回答,略有简化:

  • 对于而不是外部的
  • 内部,同时保留重复使用同一除数的能力
  • 使用itertools.groupby大大简化了compress()
  • 修复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'

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

如有疑问,请尝试"导入此内容"。

更新:根据普遍要求,下面是迭代分解(叹气):

"""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

我将使用列表理解来使两者相辅相成:

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