2011-01-30 20 views
38

Đây là câu hỏi thường xuyên diễn ra trong tất cả các chương trình, python của tôi và các cách khác. Tôi thực sự muốn giữ mã của tôi dưới 80 ký tự nếu có thể/không khủng khiếp xấu xí. Trong một ngôn ngữ như Perl, điều này không quá khó vì không gian trắng không quan trọng. Trong Python, nơi nó làm, tôi gió lên gõ đầu của tôi chống lại bức tường thường xuyên hơn tôi muốn được cố gắng để suy nghĩ của một "tốt đẹp" cách chia tách dài dòng của tôi. Vì vậy, Code rất kinh nghiệm, làm thế nào để bạn làm điều đó? Bất kỳ chiến lược chung nào bạn có thể thông báo cho tôi về?Làm thế nào để giữ mã Python dưới 80 ký tự mà không làm cho nó xấu xí?

Một vấn đề đặc biệt Tôi đang đối phó với ngay bây giờ là:

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

Khi tôi một cách tự nhiên cố gắng cắt giảm này tắt bằng Python, cách duy nhất nửa phong nha sẵn cho tôi có vẻ là:

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

Nó không có vẻ xấu, tôi đoán, nhưng nó chiếm ba dòng, mà chỉ là hoàn toàn không cần thiết. Phải có một cách tốt hơn, phải không?

Lưu ý: Tôi biết có những người bạn không thích 80 ký tự một dòng và đã tạo ra các giới hạn của riêng bạn. Tôi hiểu động lực đằng sau điều này và tôn trọng nó, nhưng 80 ký tự là giới hạn ưa thích của tôi. Xin đừng chiếm không gian cố gắng thuyết phục tôi đi đến 120 hoặc một số ở đây.

+4

Một trong những vấn đề có thể là tên của bạn quá dài. Hãy thử suy nghĩ về những cách diễn tả ngắn hơn. – Amber

+1

Tôi nghĩ tên của tôi thực sự rất dài vì các biến lớp. Nếu tôi gọi một hàm với ba biến lớp, chỉ là "tự". phần của những tên biến đó đã giết chết 15 ký tự. Về mặt rút ngắn tên nói chung, tôi thực sự chống lại việc thay đổi tên thành một cái gì đó không thể hiểu được chỉ để tiết kiệm không gian. Tên nên càng nhỏ càng tốt trong khi vẫn mô tả chính xác nó là gì. – Eli

+21

Được thăng hạng để phản đối yêu cầu của @Glenn Maynard. PEP 8 nói rõ ràng, "Giới hạn tất cả các dòng với tối đa 79 ký tự." – Johnsyweb

Trả lời

17

Cách ưa thích của gói dài dòng là bằng cách sử dụng hàm ý tiếp tục dòng Python bên trong dấu ngoặc, dấu ngoặc và dấu ngoặc. Các đường dài có thể được chia nhỏ trên nhiều dòng bằng cách biểu thức gói trong dấu ngoặc đơn. Chúng nên được sử dụng theo sở thích sử dụng dấu gạch chéo ngược cho dòng tiếp tục. Đảm bảo thụt lề dòng một cách thích hợp. Địa điểm ưa thích của để giải quyết xung quanh một toán tử nhị phân là sau toán tử , không phải trước toán tử đó.

PEP 8 Style Guide for Python Code (theo liên kết để biết ví dụ).

+9

Bản thân PEP8 có một ví dụ tuyệt vời về lý do tại sao chiều rộng cột ngắn như vậy bị phá vỡ. Lớp "Hình chữ nhật" của nó không thể đọc được vì nó là một sự tự nhại, điều này khiến cho mọi người thực sự coi nó là nghiêm túc. Đừng viết mã như thế. –

+6

Đồng ý: lớp 'Rectangle' là một ví dụ khủng khiếp, nhưng nó làm nổi bật * cách * để ngắt dòng. Tuy nhiên, PEP8 là hướng dẫn phong cách dứt khoát cho mã Python. Nếu bạn đang đi để viết mã Python, bạn nên dính vào nó. [Giống như nếu bạn định viết mã Perl, bạn nên tránh các tên biến có ý nghĩa ;-)] – Johnsyweb

+6

Không có hướng dẫn kiểu nào là "dứt khoát"; họ là những người hướng dẫn, không phải sách quy tắc, và chỉ có thể được áp dụng một cách hữu ích khi trộn với một liều ý thức mạnh mẽ. Đó là thiếu, tôi nghĩ rằng, khi tiếp tục để bọc dòng như thể 80x25 thiết bị đầu cuối vẫn là một môi trường phát triển điển hình. –

2

Hãy thử rút ngắn tên của bạn nếu bạn có tùy chọn đó. Nếu không, bạn có thể sử dụng ký tự \ để tiếp tục các dòng của mình lên dòng tiếp theo (cùng với các cấu trúc tương tự khác như những gì bạn đã đề cập ở trên).

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

'\' là bạn của bạn. Tất nhiên, bạn đã biết rằng bạn có thể chia các dòng trong một danh sách đối số tại dấu phẩy, mà không sử dụng '\'. Ngoài ra, nếu bạn có các chuỗi dài:

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

trở thành:

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

này hoạt động vì Python sẽ kết hợp xâu liền kề, bỏ qua khoảng trắng giữa các chuỗi chữ liền kề.

+3

Tôi không tin bạn khi bạn nói Python nối chuỗi chữ liền kề, vì vậy tôi đã đi và thử nghiệm nó. Nó thực sự! Liệu Python có thực sự xử lý nhiều chuỗi ký tự chuỗi liên tiếp như một chuỗi duy nhất cho các mục đích cú pháp không? –

+0

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

+1

@Ryan Thompson: Có. Điều này đã được mượn từ C. –

33

mã phong cách của bạn dường như nhấn mạnh rằng nếu bạn phá vỡ một đường bên trong một ngoặc đơn, dòng dưới đây cần phải thẳng hàng với nó:

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

Nếu bạn sẵn sàng thả yêu cầu này, bạn có thể định dạng mã như sau, trong đó các dòng tiếp tục có thụt lề kép cố định:

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

Điều này tránh viết mã xuống lề bên phải trên trang và rất dễ đọc khi bạn đã quen với nó. Nó cũng có lợi ích là nếu bạn sửa đổi tên của "SomeLongLongName", bạn không phải thụt lề lại tất cả các dòng sau đây. Một ví dụ nữa sẽ như sau:

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

Các thụt lề kép cho tiếp tục dòng cho phép bạn trực tách chúng ra khỏi dòng thụt vào bởi vì họ đang ở trong một khối if hoặc else.

Như những người khác đã lưu ý, việc sử dụng tên rút gọn cũng giúp ích, nhưng điều này không phải lúc nào cũng có thể (chẳng hạn như khi sử dụng API bên ngoài).

+2

Có. Tôi làm điều này trong tất cả các ngôn ngữ: C, Python, bất cứ điều gì. – steveha

+0

Đẹp! Tôi không có ý tưởng đó làm việc! Trong hoàn cảnh nào nó hoạt động? – Eli

+3

Khi bạn có một dòng liên tục (hoặc vì dấu ngoặc đơn vẫn mở, hoặc ký tự dấu gạch chéo ngược trên dòng trước đó, vv), python sẽ bỏ qua thụt đầu dòng trên đó. Điều này có nghĩa là bạn được tự do thụt lề dòng tiếp tục như bạn muốn. – davidg

3

Tôi là câu trả lời thứ hai của Michael Kent (và tôi đã upvoted nó).

Ngoài ra, bạn cũng nên đọc "PEP 8" và hấp thụ các bài học của nó.

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

Nhưng Python, với không gian tên của nó, tính năng mạnh mẽ, và các lớp hướng đối tượng, nên cho phép bạn sử dụng tên viết tắt của thuận tiện mọi thứ.

Trong C, bạn cần sử dụng số nhận dạng dài trong nhiều trường hợp vì tên cần phải là duy nhất trong phạm vi nhất định. Như vậy:

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

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

Trong Python, tất cả những sẽ là BUILTIN str():

temp = str(foo_flags) 

Trong C++ bạn có các lớp học và không gian tên, vì vậy bạn sẽ có thể sử dụng tính năng hướng đối tượng như trong Python, nhưng trong C bạn cần tên duy nhất trên toàn cầu, vì vậy bạn thường phải làm những thứ như thế này:

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

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

trong Python, bạn nên thể thêm chức năng thành viên, hoặc khai thác quá tải, nếu thích hợp:

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) 

Bạn cũng có thể ưu tiên các tên biến dài thực sự cho mục đích tự lập tài liệu. Tôi thích terseness:

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

Tôi đặc biệt thích phong cách đầu tiên, ngắn gọn đến thứ hai. Theo PEP 8, tôi thích các tên biến số thấp hơn (chẳng hạn như c cho cá thể Circle). Trong Python, nó cũng thường được khuyến khích sử dụng "Duck Typing" giống như tôi đã làm trong lớp terse: nếu bạn muốn bán kính là một phao, sau đó ép nó vào một float trong __init__() thay vì kiểm tra loại của nó. Tương tự, thay vì kiểm tra xem bạn có được thông qua phiên bản Point hay không, chỉ cần ép buộc bất kỳ điều gì bạn nhận được vào một số Point.Bạn đang cho phép Point.__init__() tăng ngoại lệ nếu đối số không có ý nghĩa là Point; không cần kiểm tra thêm trong Circle.__init__(). Ngoài ra, chức năng Point.__init__() của bạn có thể kiểm tra rõ ràng xem bạn đã vượt qua nó chưa, ví dụ Point và trả lại phiên bản không thay đổi, nếu thực sự tốn kém để bắt đầu Point. (Trong ví dụ này, một Point thực sự chỉ là một cặp giá trị, vì vậy nó có thể đủ nhanh để chỉ tạo lại điểm và bạn không cần kiểm tra.)

Bạn có thể nhận thấy cách kỳ lạ mà tôi đã làm chuỗi ba dòng được trích dẫn nhiều dòng. Bởi vì các quy tắc thụt lề trong Python, tôi cần phải thụt lề chuỗi được trích dẫn ba, nhưng tôi không muốn thụt lề các dòng của chuỗi bởi vì thụt lề sẽ là một phần của chuỗi. Thực sự tôi muốn tất cả các dòng khác nhau ở lề trái, vì vậy tôi có thể thấy rõ các dòng đó đang nhận được bao lâu (và đảm bảo chúng có tất cả 79 ký tự hoặc ngắn hơn). Vì vậy, tôi sử dụng dấu gạch chéo ngược để cho phép dòng đầu tiên của chuỗi nhiều dòng nằm ở lề trái với các dòng khác, mà không chèn một dòng mới ở đầu chuỗi nhiều dòng.

Dù sao, phong cách terser nghĩa tên biến của bạn và như vậy là dễ dàng hơn để gõ, và nó dễ dàng hơn để phù hợp với đường dây của bạn trong giới hạn 79-cột khuyến cáo của PEP 8.

Nó thậm chí sẽ không được hoàn toàn kinh khủng khi sử dụng tên thành viên nội bộ dài một chữ cái, trong một lớp đơn giản như thế này. Chỉ với hai thành viên, bạn có thể sử dụng khá tốt .c cho thành viên trung tâm và .r cho bán kính. Nhưng điều đó không mở rộng tốt và .center.radius vẫn dễ dàng nhập và dễ nhớ.

Cũng là một ý tưởng rất hay khi đưa các tài liệu thông tin. Bạn có thể sử dụng tên hơi terse, nhưng có giải thích dài hơn trong 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 

Không gian tên thật tuyệt. Lưu ý mức độ rõ ràng của nó khi chúng tôi sử dụng math.pi; bạn biết nó là hằng số toán học, và bạn có thể có một số biến cục bộ pi (đối với "Chỉ mục chương trình" có lẽ) và nó không va chạm với hằng số toán học.

5

Một số người đã trích dẫn lớp Hình chữ nhật làm ví dụ kém. Ví dụ này trong pep8 là không phải cách duy nhất để thực hiện việc này.

gốc:

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) 

Đây là cách tôi sẽ viết nó.

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) 

Lý do hạnh phúc là:

  • thụt đầu dòng bổ sung để thẳng hàng với '(' là một sự lãng phí thời gian nếu soạn thảo của bạn không làm điều đó cho bạn và khó khăn hơn để đọc vì có như vậy là IMO không gian trắng hàng đầu
  • Tôi cố gắng nghỉ ngơi càng sớm càng tốt trừ khi có lý do thuyết phục trong logic mã. dòng ... trùng hợp rất xấu! Hai dòng tiếp tục thụt lề giải quyết vấn đề này.
  • Tôi thích tránh nếu lý do phải sử dụng tiếp tục dòng là cố gắng làm quá nhiều trên một dòng.Ví dụ ở đây là ValueError, nơi chúng được định dạng bằng cách sử dụng toán tử định dạng chuỗi. Tôi đặt msg thay thế. (Lưu ý: Định dạng Chuỗi sử dụng phương pháp định dạng được ưu tiên và % không còn được dùng kể từ 3.1).
1

tôi thấy mình sử dụng nhiều và trung cấp nhiều biến, đó không chỉ giúp giữ trong vòng 80 ký tự, nhưng làm cho mã dễ đọc hơn bằng cách cho chúng những cái tên mô tả như:

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

hơn:

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

Bạn có thể làm điều này với mô-đun lâu và lớp tên quá

method = SomeLongClassName.SomeLongMethodName 
self.SomeLongLongName = method(some_obj, self.user1, self.user2) 
Các vấn đề liên quan