初学者想知道他的代码是否是" 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