Подтвердить что ты не робот

Новичок задавался вопросом, является ли его код "Pythonic"

Это действительно первое, что я написал на python. Я исхожу из фона Java. Я не хочу просто узнать, как программировать Java-код с синтаксисом Python. Я хочу научиться программировать в питонической парадигме.

Не могли бы вы, ребята, прокомментировать, как я могу сделать следующий код более 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 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))
4b9b3361

Ответ 1

Существует превосходный праймер Дэвида Гудгера, называемый "Code Like a Pythonista" здесь. Несколько вещей из этого переименования текста (цитирование):

  • joined_lower для функций, методов, атрибуты

  • joined_lower или ALL_CAPS для Константы

  • StudlyCaps для классов

  • camelCase только для соответствия ранее существовавшие соглашения

Ответ 2

Просто используйте 'import math' и 'math.sqrt()' вместо 'from math import sqrt' и 'sqrt()'; вы ничего не выигрываете, просто импортируя "sqrt", а код быстро становится громоздким со слишком большим количеством из-импортов. Кроме того, такие вещи, как reload() и издевательство над тестами, ломаются намного быстрее при многократном использовании импорта.

Функция divmod() является удобным способом выполнения как деления, так и по модулю. Вы можете использовать для /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), вам придется обернуть вызов факторам() в списке().

Ответ 3

Еще одна вещь, которую вы, возможно, захотите посмотреть, - это docstring. Например, комментарий для этой функции:

# 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

Ответ 4

Несколько комментариев:

  • Я бы заменил range() на xrange(); когда вы вызываете range(), он выделяет весь диапазон одновременно, тогда как при повторении итерации по xrange() он возвращает каждый результат по одному, сохраняя память.
  • Не помещайте выражения после условных обозначений в одну строку (if num2s -- 0: return factorList). Это затрудняет обзор с первого взгляда, что он делает (это блок).
  • Не бойтесь использовать модули. В модуле [sympy][1] уже есть код для вычисления факторов, который может упростить ваш код, исключив большую часть его.
  • Форматирование строки Python прост и эффективен.

Например:

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

можно изменить на

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

В целом, я не нахожу, что ваш код сильно не-pythonic. Просто убедитесь, что вы хотите использовать разделение полов, потому что это то, что обычно происходит по умолчанию с целыми значениями. В противном случае вам нужно будет исправить оператор деления:

from __future__ import division

Иногда - разочарование в отношении языка.

Ответ 5

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 совместимое именование
  • slicing (и присвоение срезам)
  • итераторы
  • форматирование строк

Функция предполагает, что вход упорядочен, что выполняется факторами.

Изменить: удалены специальные случаи для некоторых списков, более компактные таким образом

Ответ 6

На основе ответа на крис слегка упрощается:

  • вместо внешнего
  • внутренний, чтобы сохранить возможность повторного использования одного и того же делителя.
  • использовать itertools.groupby упрощает compress() намного
  • исправить небольшую ошибку в tostring()

НТН:

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'

Ответ 7

Не бойтесь понимания списков. Переключение с Java на Python и обнаружение их было хорошим днем.

Для функции факторов может быть что-то вроде этого:

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

Вероятно, это не лучший код, но он короткий и понятный.

Удачи с Python, это отличный язык.

Ответ 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)

Ответ 9

Вот что выпрыгивает на меня:

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] называется пониманием списка.

Ответ 10

Поскольку этот пост, кажется, воскрешен Кейси (lol), я добавлю свои 2 цента.

Перейдите все в PEP-8. Это помогло мне существенно, когда у меня были проблемы с форматированием кода.

Ответ 11

Я бы воспользовался списком, чтобы получить два раза:

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, которые ваш код не сделал. То, что вы делаете с twos, кажется арбитражным (иногда вы получаете строку, иногда число, иногда ничего), поэтому я решил, что все будет хорошо. Вы можете легко изменить это, если хотите:

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

Ответ 12

Использование рекурсии (где не обязательно) не является питоновой. У Python нет исключения рекурсии хвоста, а плоскость лучше, чем вложенная.

Если вы сомневаетесь, попробуйте import this

update: по популярному запросу здесь идет итеративная факторизация (вздох):

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