Как сохранить код 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 или некоторые из них здесь.
Ответы
Ответ 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)