2011-01-30 7 views
38

Esta es una pregunta que se repite en toda mi programación, python y demás. Realmente me gusta mantener mi código bajo 80 caracteres si es posible/no horriblemente feo. En un lenguaje como Perl, esto no es demasiado difícil ya que el espacio en blanco no importa. En Python, donde lo hace, termino golpeando mi cabeza contra la pared con más frecuencia de lo que me gustaría pensar en una "buena" forma de dividir mis largas filas. Entonces, gurús del código, ¿cómo lo hacen? ¿Alguna estrategia general que me puedas informar?¿Cómo puedo mantener el código de Python por debajo de 80 caracteres sin hacerlo feo?

Un problema particular que estoy tratando ahora es:

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

Cuando yo, naturalmente, tratar de cortar esto en Python, el camino sólo la mitad decente a mi disposición parece ser:

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

No parece tan malo, supongo, pero ocupa tres líneas, lo cual es completamente innecesario. Debe haber una mejor manera, ¿no?

Nota: Sé que hay quienes no le gustan los 80 caracteres por línea y han creado sus propios límites. Entiendo la motivación detrás de esto y lo respeto, pero 80 caracteres es mi límite preferido. Por favor, no ocupe espacio tratando de convencerme de ir a 120 o algo así aquí.

+4

Uno de los problemas puede ser el hecho de que sus nombres son tan largos. Trata de pensar en formas más cortas de describir las cosas. – Amber

+1

Creo que mis nombres realmente terminan por las variables de clase. Si estoy llamando a una función con tres variables de clase, solo el "yo". una porción de esos nombres variables ya mata 15 caracteres. En términos de reducción de nombre en general, estoy realmente en contra de cambiar un nombre a algo ininteligible solo para ahorrar espacio. Un nombre debe ser tan pequeño como sea posible sin dejar de describir razonablemente qué es. – Eli

+21

Subida al contador como desventaja de @Glenn Maynard. PEP 8 establece claramente, "Limitar todas las líneas a un máximo de 79 caracteres". – Johnsyweb

Respuesta

17

La forma preferida de envolver largas líneas es mediante el uso implícito continuación línea de Python dentro de paréntesis, corchetes y llaves. Las líneas largas pueden dividirse en varias líneas por envolviendo expresiones entre paréntesis. Se deben usar con preferencia a usando una barra diagonal inversa para la línea continuación. Asegúrese de sangrar la línea continua de manera adecuada. El lugar preferido para romper alrededor de un operador binario es después de el operador , no antes.

PEP 8 Style Guide for Python Code (siga el enlace para ver ejemplos).

+9

PEP8 en sí tiene un brillante ejemplo de por qué un ancho de columna tan corto está tan roto. Su clase "Rectángulo" es ilegible hasta el punto de ser una autoparodia, lo que hace que resulte desconcertante que las personas realmente lo tomen en serio. No escriba código así. –

+6

De acuerdo: la clase 'Rectangle' es un terrible ejemplo, pero resalta * cómo * para romper líneas. Sin embargo, PEP8 es la guía de estilo definitiva para el código de Python. Si va a escribir el código de Python, debe atenerse a él. [Al igual que si va a escribir el código Perl, debe evitar los nombres de variables significativas ;-)] – Johnsyweb

+6

Ninguna guía de estilo es "definitiva"; son guías, no libros de reglas, y solo pueden aplicarse de manera útil cuando se mezclan con una fuerte dosis de sentido común. Creo que eso no funciona cuando continuamos envolviendo las líneas como si los terminales 80x25 siguieran siendo un entorno de desarrollo típico. –

2

Intente acortar sus nombres si tiene esa opción. De lo contrario, puede usar el carácter \ para continuar sus líneas en la línea siguiente (junto con otras construcciones similares, como las mencionadas anteriormente).

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

'\' es tu amigo. Por supuesto, ya sabes que puedes dividir líneas en una lista de argumentos en comas, sin usar '\'. Además, si usted tiene cadenas largas:

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

se convierte en:

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

Esto funciona porque Python combinará literales de cadena adyacentes, ignorando espacios en blanco entre las cadenas literales adyacentes.

+3

No te creí cuando dijiste que Python concatena literales de cadenas adyacentes, así que fui y lo probé. ¡Realmente lo hace! ¿Realmente Python trata múltiples literales secuenciales consecutivos como una sola cadena para propósitos sintácticos? –

+0

@Ryan: [Sí] (http://docs.python.org/reference/lexical_analysis.html#string-literal-concatenation). – ephemient

+1

@Ryan Thompson: Sí. Esto fue tomado prestado de C. –

33

Su estilo de código parece insistir en que si se rompe una línea dentro de un paréntesis, líneas de abajo tienen que alinearse con él:

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

Si no está dispuesto a dejar este requisito, puede dar formato al código de la siguiente manera, donde las líneas continuas tienen una doble guión fijo:

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

Esto evita la escritura de código por el margen derecho de la página, y es muy fácil de leer una vez que se acostumbre a ella. También tiene la ventaja de que si modifica el nombre de "SomeLongLongName", no tiene que volver a aplicar sangría a todas las líneas siguientes. Un ejemplo más sería de la siguiente manera:

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

El guión doble para líneas continuas que permite separar visualmente de líneas sangradas porque están en un bloque de if o else.

Como otros han notado, usar nombres en corto también ayuda, pero esto no siempre es posible (como cuando se usa una API externa).

+2

Sí. Hago esto en todos los idiomas: C, Python, lo que sea. – steveha

+0

¡Agradable! ¡No tenía idea de que funcionó! ¿En qué circunstancias funciona? – Eli

+3

Cuando tiene una línea continua (ya sea porque todavía hay un paréntesis abierto, o un carácter de barra diagonal inversa en la línea anterior, etc.), python ignorará la sangría en él. Esto significa que puede escribir libremente líneas continuas como lo desee. – davidg

3

Recibí la respuesta de Michael Kent por segunda vez (y la volví a subir).

Pero también, debe leer "PEP 8" y absorber sus lecciones.

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

Pero Python, con sus espacios de nombres, características de gran alcance, y las clases orientadas a objetos, se debe dejar que se utiliza convenientemente nombres cortos para las cosas.

En C, necesita utilizar identificadores largos en muchos casos porque los nombres deben ser únicos dentro de un ámbito determinado. Por lo tanto:

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

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

En Python, todos estos serían la orden interna str():

temp = str(foo_flags) 

En C++ tiene clases y espacios de nombres, por lo que debe ser capaz de utilizar las funciones orientadas a objetos como en Python, pero en C necesita nombres únicos a nivel mundial, por lo que a menudo tienen que hacer cosas como esta:

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

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

en Python, debe o bien agregar funciones miembro u operadores de sobrecarga, según el caso:

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) 

También puede preferir nombres de variables realmente largos para fines de auto-documentación. Prefiero concisión:

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()) 

Yo prefiero el primer estilo rápido al segundo. Según PEP 8, me gustan los nombres de variables en minúsculas (como c para la instancia Circle). En Python, también se recomienda generalmente usar "Duck Typing" como hice en la clase terse: si quieres que el radio sea un flotante, entonces coacciona a float en __init__() en lugar de verificar su tipo. Del mismo modo, en lugar de verificar si se aprobó una instancia de Point, simplemente fuerce lo que obtenga a Point.Está dejando que Point.__init__() presente una excepción si el argumento no tiene sentido como Point; no hay necesidad de un cheque adicional en Circle.__init__(). Además, su función Point.__init__() puede verificar explícitamente para ver si le pasó una instancia de Point y devolver la instancia sin cambios, si es realmente costoso iniciar un Point. (En este ejemplo, un Point es realmente solo un par de valores, por lo que probablemente sea lo suficientemente rápido para volver a crear el punto y no necesita el cheque.)

Puede notar la forma extraña en que hice el cadena multi-línea de citas triples Debido a las reglas de sangrado en Python, necesité sangrar la cadena de comillas triples, pero no quiero aplicar sangría a las líneas de la cadena porque la sangría sería parte de la cadena. Realmente quiero que todas las líneas múltiples estén en el margen izquierdo, así puedo ver claramente cuánto tiempo están obteniendo esas líneas (y me aseguro de que sean todas 79 caracteres o más cortas). Así que utilizo el escape de barra invertida para permitir que la primera línea de la cadena de varias líneas esté en el margen izquierdo con las otras líneas, sin insertar una línea nueva al comienzo de la cadena de varias líneas.

De todos modos, el estilo más concisa significa que los nombres de las variables y tales son más fáciles de escribir, y es más fácil para adaptarse a sus líneas en el límite de 79 columnas recomendadas por PEP 8.

Ni siquiera sería completamente Es horrible usar nombres de miembros internos que tienen una sola letra, en una clase tan simple como esta. Con solo dos miembros, puede usar .c para el miembro central y .r para el radio. Pero eso no se escala bien, y .center y .radius son fáciles de escribir y fáciles de recordar.

También es una muy buena idea poner docstrings informativos. Puede usar nombres algo escuetos, pero tener explicaciones más largas en la 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 

Los espacios de nombres son geniales. Observe qué tan claro es cuando usamos math.pi; usted sabe que es la constante matemática, y podría tener alguna variable local pi (para el "Índice del programa") y no colisiona con la constante matemática.

5

Algunas personas citaban la clase Rectangle como un mal ejemplo. Este ejemplo en el pep8 es no la única manera de hacerlo.

original:

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) 

Ésta es la forma en que lo escribiría.

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) 

La razón es:

  • sangría adicional para alinearse con '(' es una pérdida de tiempo si el editor no lo está haciendo para usted y más difícil de leer ya que no es tan mucho espacio blanco líder IMO
  • Trato de romper lo más tarde posible a menos que haya una razón de peso en la lógica del código
  • Alineando con el '(' en este caso se creó exactamente el mismo nivel de sangría que el siguiente línea ... muy mala coincidencia! Las líneas de continuación de doble sangría resuelven este problema.
  • Prefiero evitarlo si la razón para tener que usar la continuación de línea es tratar de hacer demasiado en una línea.El ejemplo aquí es el ValueError donde están formateando usando el operador de formato de cadena. Configuro msg en su lugar. (Nota: las cadenas de formato que utilizan el método de formato son las preferidas, y % está en desuso desde 3.1).
1

me encuentro con más y más intermedios de variables, que no sólo ayudan a mantenerse a menos de 80 caracteres, pero hacer el código más legible dando cosas nombres descriptivos como:

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

en lugar de:

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

Usted puede hacer esto con el módulo de largo y de clase nombres también

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