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

Как сохранить код Python под 80 символами, не делая это ужасно?

Это вопрос, который повторяется во всех моих программах, python и других. Мне очень нравится держать мой код под 80 символами, если это вообще возможно/не ужасно уродливо. На языке, подобном Perl, это не слишком сложно, поскольку пустое пространство не имеет значения. В Python, где это происходит, я заставляю себя чаще стучать головой о стену, чем хотелось бы думать о "хорошем" способе разделения моих длинных строк. Итак, Гуру кода, как вы это делаете? Любые общие стратегии, которые вы можете сообщить мне?

Частная проблема, с которой я имею дело сейчас:

self.SomeLongLongName = SomeLongLongName.SomeLongLongName(some_obj, self.user1, self.user2)

Когда я, естественно, пытаюсь вырезать это в Python, единственный доступный мне доступ к получению:

self.SomeLongLongName = SomeLongLongName.SomeLongLongName(some_obj,
                                                          self.user1
                                                          self.user2)

Это не выглядит так плохо, я думаю, но он занимает три строки, что просто совершенно не нужно. Должен быть лучший способ, нет?

Примечание. Я знаю, что есть те из вас, кто не любит 80 символов и создает свои собственные лимиты. Я понимаю мотивацию этого и уважаю его, но 80 символов - это мой предпочтительный предел. Пожалуйста, не занимайте место, пытаясь убедить меня переехать на 120 или некоторые из них здесь.

4b9b3361

Ответ 1

Предпочтительный способ обертывания линии - это использование Python подразумеваемых продолжение строки в круглых скобках, скобки и брекеты. Длинные линии могут разбиваться на несколько строк на обертывание выражений в круглых скобках. Они должны использоваться в предпочтении используя обратную косую черту для линии продолжение. Убедитесь, что продолжение линии. предпочтительное место для бинарный оператор после оператора, а не до него.

Руководство по стилю PEP 8 для кода Python (см. ссылку для примеров).

Ответ 2

Ваш стиль кода, похоже, настаивает на том, что если вы нарушаете строку внутри скобки, строки ниже должны совпадать с ней:

self.SomeLongLongName = SomeLongLongName.SomeLongLongName(some_obj,
                                                          self.user1
                                                          self.user2)

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

self.SomeLongLongName = SomeLongLongName.SomeLongLongName(
        some_obj, self.user1, self.user2)

Это позволяет избежать написания кода по правому краю на странице и очень легко читается после того, как вы привыкли к нему. Также имеет смысл, что, если вы измените имя "SomeLongLongName", вам не нужно повторно отступать все следующие строки. Более длинный пример:

if SomeLongLongName.SomeLongLongName(
        some_obj, self.user1, self.user2):
    foo()
else:     
    bar()

Двойной отступ для продолженных строк позволяет визуально отделить их от отступов строк, поскольку они находятся в блоке if или else.

Как отмечали другие, использование сокращенных имен также помогает, но это не всегда возможно (например, при использовании внешнего API).

Ответ 3

self.SomeLongLongName = SomeLongLongName.\
    SomeLongLongName(some_obj, self.user1, self.user2)

'\' твой друг. Конечно, вы уже знаете, что вы можете разделить строки в списке аргументов запятыми, не используя '\'. Кроме того, если у вас длинные строки:

myLongString = "This is a really long string that is going to be longer than 80 characters so oh my what do I do to make this work out?"

становится:

myLongString = "This is a really long string that is going to be longer than"\
    " 80 characters so oh my what do I do to make this work out?"

Это работает, потому что Python будет комбинировать смежные строковые литералы, игнорируя пробелы между смежными литеральными строками.

Ответ 4

Некоторые люди цитировали класс Rectangle как плохой пример. Этот пример в pep8 не - единственный способ сделать это.

Оригинал:

class Rectangle(Blob):

    def __init__(self, width, height,
                 color='black', emphasis=None, highlight=0):
        if (width == 0 and height == 0 and
            color == 'red' and emphasis == 'strong' or
            highlight > 100):
            raise ValueError("sorry, you lose")
        if width == 0 and height == 0 and (color == 'red' or
                                           emphasis is None):
            raise ValueError("I don't think so -- values are %s, %s" %
                             (width, height))
        Blob.__init__(self, width, height,
                      color, emphasis, highlight)

Вот как писал бы I.

class Rectangle(Blob):

    def __init__(self, width, height, color='black', emphasis=None,
            highlight=0):
        if (width == 0 and height == 0 and color == 'red' and
                emphasis == 'strong' or highlight > 100):
            raise ValueError("sorry, you lose")
        if width == 0 and height == 0 and (color == 'red' or 
                emphasis is None):
            msg = "I don't think so -- values are %s, %s" % (width, height)     
            raise ValueError(msg)
        Blob.__init__(self, width, height, color, emphasis, highlight)

Причиной является:

  • Дополнительный отступ для выравнивания с '(' - это пустая трата времени, если ваш редактор не делает это для вас и более трудный для чтения, поскольку существует так много ведущих ИМО ИО.
  • Я пытаюсь сломать как можно раньше, если в логике кода нет веской причины.
  • Выравнивание с помощью '(' в этом случае создало тот же самый уровень отступа, что и следующая строка... очень плохое совпадение. Двойные строки отступов продолжения решают эту проблему.
  • Я предпочитаю избегать, если причина использования продолжения строки пытается сделать слишком много на одной строке. Примером здесь является ValueError, где они форматируются с использованием оператора строкового формата. Вместо этого я установил msg. (Примечание. Форматирование строк с использованием метода форматирования является предпочтительным, а % считается устаревшим с 3.1).

Ответ 5

Второй ответ Майкла Кента (и я его поддержал).

Но также вы должны прочитать "PEP 8" и освоить свои уроки.

http://www.python.org/dev/peps/pep-0008/

Но Python, с его пространствами имен, мощными функциями и объектно-ориентированными классами, должен позволить вам использовать короткие короткие имена для вещей.

В C вам нужно использовать длинные идентификаторы во многих случаях, потому что имена должны быть уникальными в пределах данной области. Таким образом:

char *StringFromInt(int x);
char *StringFromFloat(float x);
char *StringFromUnsigned(unsigned int x);

char *str_temp = strdup(StringFromUnsigned(foo_flags));

В Python все это будет встроенным str():

temp = str(foo_flags)

В С++ у вас есть классы и пространства имен, поэтому вы должны иметь возможность использовать объектно-ориентированные функции, как в Python, но в C вам нужны глобально уникальные имена, поэтому вам часто приходится делать такие вещи, как это:

typedef struct s_foo
{
   // struct members go here
} FOO;

FooAdd();
FooSubtract();
StringFromFoo();

В Python вам следует либо добавить функции-члены, либо операторы перегрузки:

class Foo(object):
    def __init__(self):
        # member variables initialized here
    def add(self, x):
        # add x to a Foo
    def subtract(self, x):
        # subtract x from a Foo
    def __str___(self):
        # return a string that represents a foo

f = Foo()
f.add(x)
f.sub(y)
# the following two both use __str__()
temp = str(f)
print(f)

Вы также можете использовать очень длинные имена переменных для целей самодокументирования. Я предпочитаю терпение:

import math

class Circle(object):
    """\
Circle: a class representing a circle in a plane.
Includes the following member functions:
    area() -- return the area of the circle"""
    def __init__(self, center=Point([0, 0]), radius=0.0):
        """\
Circle(center, radius)
center must be an instance of class Point() or convertible to Point()
radius must be an int or float and must not be negative"""
        if radius < 0:
            raise ValueError("radius must be >= 0")
        self.center = Point(center)
        self.radius = float(radius)
    def area(self):
        "returns area as a float."
         return math.pi * self.radius ** 2

c = Circle([23, 45], 0.5)
print(c.area())


class CircleGraphicsObject(object):
    def __init__(self, CenterOfTheCircle, RadiusOfTheCircle):
        # init code goes here
    def AreaOfTheCircle(self):
        return math.pi * self.RadiusOfTheCircle ** 2

CircleInstance = CircleGraphicsObject(PointObject([23, 45]), 0.5)
print(CircleInstance.AreaOfTheCircle())

Я сильно предпочитаю первый, краткий стиль второму. В соответствии с PEP 8 мне нравятся имена переменных с более низким регистром (например, c для экземпляра Circle). В Python также рекомендуется использовать "Duck Typing", как это было сделано в кросс-классе: если вы хотите, чтобы радиус был плавающим, то принудите его к float в __init__() вместо проверки его типа. Аналогично, вместо того, чтобы проверять, был ли вы передан экземпляр Point, просто принуждайте все, что вы получаете к Point. Вы позволяете Point.__init__() создавать исключение, если аргумент не имеет смысла как Point; нет необходимости в дополнительной проверке в Circle.__init__(). Кроме того, ваша функция Point.__init__() может явно проверить, не передал ли она экземпляр Point и вернуть экземпляр без изменений, если действительно стоит инициализировать Point. (В этом примере Point - это всего лишь пара значений, поэтому он, вероятно, достаточно быстрый, чтобы просто повторно создать точку, и вам не нужна проверка.)

Возможно, вы заметили нечетный способ создания многострочной строки с тремя строками. Из-за правил отступа в Python мне нужно было отложить строку с тремя кавычками, но я не хочу отступать от строк строки, потому что отступ будет частью строки. На самом деле я хочу, чтобы все несколько строк находились на левом краю, поэтому я могу четко видеть, как долго эти линии получаются (и убедитесь, что они все 79 символов или короче). Поэтому я использую escape-обратную косую черту, чтобы первая строка многострочной строки была на левом поле с другими строками, не вставляя новую строку в начале многострочной строки.

Во всяком случае, стиль terser означает ваши имена переменных, и их легче набирать, и их легче поместить в лимит 79 столбцов, рекомендованный PEP 8.

Не было бы даже ужасно использовать внутренние имена членов, которые имеют одну букву длиной, в таком простом классе. Вы можете использовать .c только для двух членов центра, а для радиуса - .r. Но это не масштабируется хорошо, а .center и .radius по-прежнему легко печататься и легко запоминаться.

Это также очень хорошая идея, чтобы поставить информативные докстры. Вы можете использовать несколько кратких имен, но имеют более длинные объяснения в docstring.

class Foo(object):
    # init goes here
    def area(self):
        "returns area as a float."
         return self.area

class VerboseFoo(object):
    # init goes here
    def AreaAsFloat(self):
        return self.FloatAreaValue

Пространства имен велики. Обратите внимание, насколько это ясно, когда мы используем math.pi; вы знаете, что это постоянная математика, и вы можете иметь некоторую локальную переменную pi (возможно, для "индекса программы" ), и она не сталкивается с постоянной математики.

Ответ 6

Попробуйте сократить свои имена, если у вас есть этот параметр. В противном случае вы можете использовать символ \, чтобы продолжить свои строки на следующей строке (наряду с другими аналогичными конструкциями, такими как вы упомянули выше).

Ответ 7

Я нахожу себя использующим все более и более промежуточных переменных, которые не только помогают оставаться в пределах 80 символов, но и делают код более читаемым, давая вещи описательным именам, например:

old_name = 'reallylonguglypath/to/current/file.foo'
new_name = 'evenmoreuglylong/to/new/desination/for/file.foo' 
os.rename(old_name, new_name)

а не:

os.rename("reallylonguglypath/to/current/file.foo",
              "evenmoreuglylong/to/new/desination/for/file.foo")

Вы можете сделать это с длинными именами модулей и классов.

method = SomeLongClassName.SomeLongMethodName
self.SomeLongLongName = method(some_obj, self.user1, self.user2)