初学者想知道他的代码是否是" Pythonic"
这确实是我用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 ^ 0和2 ^ 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

