Python là một ngôn ngữ cấp độ trống

Một chương trình Python được đọc bởi trình phân tích cú pháp. Python được thiết kế để trở thành một ngôn ngữ dễ đọc. Cú pháp của ngôn ngữ lập trình Python là tập hợp các quy tắc xác định cách viết chương trình Python

Cấu trúc dòng Python

Một chương trình Python được chia thành một số dòng logic và mỗi dòng logic được kết thúc bằng mã thông báo NEWLINE. Một dòng logic được tạo từ một hoặc nhiều dòng vật lý
Một dòng chỉ chứa khoảng trắng, tab, nguồn cấp dữ liệu biểu mẫu có thể là nhận xét, được gọi là dòng trống và trình thông dịch Python sẽ bỏ qua dòng đó
Một dòng vật lý là một chuỗi các ký tự được kết thúc bằng một chuỗi cuối dòng [trong windows, nó được gọi là CR LF hoặc return theo sau là nguồn cấp dữ liệu và trong Unix, nó được gọi là LF hoặc nguồn cấp dữ liệu]. Xem ví dụ sau

 

Bình luận trong Python

Nhận xét bắt đầu bằng ký tự băm [#] không phải là một phần của chuỗi ký tự và kết thúc ở cuối dòng vật lý. Tất cả các ký tự sau ký tự # cho đến cuối dòng là một phần của nhận xét và trình thông dịch Python sẽ bỏ qua chúng. Xem ví dụ sau. Cần lưu ý rằng Python không có cơ sở nhận xét nhiều dòng hoặc khối

 

Nối hai dòng

Khi bạn muốn viết một mã dài trong một dòng, bạn có thể ngắt dòng logic thành hai hoặc nhiều dòng vật lý bằng cách sử dụng ký tự dấu gạch chéo ngược [\]. Do đó, khi một dòng vật lý kết thúc bằng ký tự gạch chéo ngược [\] và không phải là một phần của chuỗi ký tự hoặc nhận xét thì nó có thể nối với một dòng vật lý khác. Xem ví dụ sau

 

Nhiều câu lệnh trên một dòng

Bạn có thể viết hai câu lệnh riêng biệt thành một dòng bằng cách sử dụng ký tự dấu chấm phẩy [;] giữa hai dòng

 

thụt đầu dòng

Python sử dụng khoảng trắng [dấu cách và tab] để xác định các khối chương trình trong khi các ngôn ngữ khác như C, C++ sử dụng dấu ngoặc nhọn [{}] để biểu thị các khối mã cho lớp, hàm hoặc điều khiển luồng. Số lượng khoảng trắng [khoảng trắng và tab] trong thụt đầu dòng không cố định, nhưng tất cả các câu lệnh trong khối phải có cùng số lượng được thụt lề. Trong chương trình sau, các câu lệnh khối không có thụt đầu dòng

 

Đây là một chương trình có thụt lề một dấu cách

 

Đây là một chương trình có thụt lề tab đơn

 

Đây là một chương trình khác có thụt vào một dấu cách + một tab

 

Phong cách mã hóa Python

  • Sử dụng 4 dấu cách cho mỗi lần thụt đầu dòng và không có tab
  • Không trộn lẫn các tab và không gian. Các tab tạo ra sự nhầm lẫn và bạn chỉ nên sử dụng dấu cách
  • Chiều dài dòng tối đa. 79 ký tự hỗ trợ người dùng với màn hình nhỏ
  • Sử dụng các dòng trống để phân tách các định nghĩa lớp và hàm cấp cao nhất và một dòng trống duy nhất để phân tách các định nghĩa phương thức bên trong một lớp và các khối mã lớn hơn bên trong các hàm
  • Khi có thể, hãy đặt nhận xét nội tuyến [nên là câu hoàn chỉnh]
  • Sử dụng khoảng trắng xung quanh các biểu thức và câu lệnh

Các từ dành riêng cho Python

Các mã định danh sau đây được sử dụng làm từ dành riêng của ngôn ngữ và không thể được sử dụng làm mã định danh thông thường

FalseclassfinallyisreturnNonecontinueforlambdatryTruedeffromnonlocalwhileanddelglobalnotwithaseliforyieldassertelseimportpass breakexceptinraise 

Trước. Lập trình CGI
Kế tiếp. Biến Python

Kiểm tra kỹ năng Python của bạn với bài kiểm tra của w3resource



Theo dõi chúng tôi trên FacebookTwitter để cập nhật thông tin mới nhất.

con trăn. Lời khuyên trong ngày

người trang trí

Trình trang trí là một đại diện thanh lịch khác của cú pháp tối giản và biểu cảm của Python

Bằng cách sử dụng các bộ trang trí, bạn có thể thay đổi hành vi hoặc kết quả của hàm mà không thực sự sửa đổi nó

Nói cách khác, các nhà trang trí trang trí các chức năng để làm cho chúng đẹp hơn theo một cách nào đó

Trình trang trí bắt đầu bằng dấu @ trong cú pháp Python và được đặt ngay trước hàm

def adder[x, y]: 
    return x+y
print[adder[10, 5]]

đầu ra

15

Bây giờ, không chạm vào chức năng ban đầu, hãy trang trí nó để nó nhân kết quả với 100

Cú pháp đơn giản, dễ học của Python có thể đánh lừa các nhà phát triển Python -- đặc biệt là những người mới làm quen với ngôn ngữ này -- bỏ sót một số điểm tinh tế và đánh giá thấp sức mạnh của ngôn ngữ này. Trong bài viết này, Martin Chikilian của Toptal trình bày danh sách "10 lỗi hàng đầu" về những lỗi hơi tế nhị, khó phát hiện hơn có thể khiến ngay cả nhà phát triển Python cao cấp nhất cũng mắc phải.

Chia sẻ

Chia sẻ

Cú pháp đơn giản, dễ học của Python có thể đánh lừa các nhà phát triển Python -- đặc biệt là những người mới làm quen với ngôn ngữ này -- bỏ sót một số điểm tinh tế và đánh giá thấp sức mạnh của ngôn ngữ này. Trong bài viết này, Martin Chikilian của Toptal trình bày danh sách "10 lỗi hàng đầu" về những lỗi hơi tế nhị, khó phát hiện hơn có thể khiến ngay cả nhà phát triển Python cao cấp nhất cũng mắc phải.

Bởi Martin Chikilian

Martin là một kỹ sư full-stack và đã làm việc với tư cách là nhà phát triển Python chuyên nghiệp từ năm 2007

Đọc phiên bản tiếng Tây Ban Nha của bài viết này được dịch bởi Marisela Ordaz

Giới thiệu về Python

Python là ngôn ngữ lập trình cấp cao, hướng đối tượng, thông dịch với ngữ nghĩa động. Các cấu trúc dữ liệu tích hợp ở mức độ cao, kết hợp với kiểu gõ động và liên kết động, khiến nó trở nên rất hấp dẫn đối với Phát triển ứng dụng nhanh, cũng như để sử dụng làm ngôn ngữ kịch bản hoặc ngôn ngữ keo để kết nối các thành phần hoặc dịch vụ hiện có. Python hỗ trợ các mô-đun và gói, do đó khuyến khích mô-đun hóa chương trình và tái sử dụng mã

Về bài viết này

Cú pháp đơn giản, dễ học của Python có thể đánh lừa các nhà phát triển Python – đặc biệt là những người mới làm quen với ngôn ngữ này – khiến họ bỏ lỡ một số nét tinh tế của nó và đánh giá thấp sức mạnh của ngôn ngữ Python đa dạng

Với ý nghĩ đó, bài viết này trình bày danh sách “10 lỗi hàng đầu” về những lỗi hơi tế nhị, khó phát hiện hơn có thể ảnh hưởng đến cả một số nhà phát triển Python cao cấp hơn ở phía sau.

[Ghi chú. Bài viết này dành cho đối tượng cao cấp hơn so với Những sai lầm phổ biến của lập trình viên Python, hướng nhiều hơn đến những người mới làm quen với ngôn ngữ này. ]

Sai lầm thường gặp #1. Sử dụng sai biểu thức làm giá trị mặc định cho đối số hàm

Python cho phép bạn chỉ định rằng đối số hàm là tùy chọn bằng cách cung cấp giá trị mặc định cho nó. Mặc dù đây là một tính năng tuyệt vời của ngôn ngữ, nhưng nó có thể dẫn đến một số nhầm lẫn khi giá trị mặc định có thể thay đổi được. Ví dụ, hãy xem xét định nghĩa hàm Python này

>>> def foo[bar=[]]:        # bar is optional and defaults to [] if not specified
..    bar.append["baz"]    # but this line could be problematic, as we'll see...
..    return bar

Một sai lầm phổ biến là nghĩ rằng đối số tùy chọn sẽ được đặt thành biểu thức mặc định đã chỉ định mỗi khi hàm được gọi mà không cung cấp giá trị cho đối số tùy chọn. Ví dụ, trong đoạn mã trên, người ta có thể mong đợi rằng việc gọi

>>> class A[object]:
..     x = 1
...
>>> class B[A]:
..     pass
...
>>> class C[A]:
..     pass
...
>>> print A.x, B.x, C.x
1 1 1
0 lặp đi lặp lại [i. e. , mà không chỉ định đối số
>>> class A[object]:
..     x = 1
...
>>> class B[A]:
..     pass
...
>>> class C[A]:
..     pass
...
>>> print A.x, B.x, C.x
1 1 1
1] sẽ luôn trả về
>>> class A[object]:
..     x = 1
...
>>> class B[A]:
..     pass
...
>>> class C[A]:
..     pass
...
>>> print A.x, B.x, C.x
1 1 1
2, vì giả định là mỗi lần _______3__0 được gọi [không chỉ định đối số ___3_______1] _____________1 được đặt thành ____3_______6 [i. e. , một danh sách trống mới]

Nhưng hãy xem điều gì thực sự xảy ra khi bạn làm điều này

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]

Huh?

Câu trả lời lập trình Python nâng cao hơn là giá trị mặc định cho đối số hàm chỉ được đánh giá một lần, tại thời điểm hàm được xác định. Do đó, đối số

>>> class A[object]:
..     x = 1
...
>>> class B[A]:
..     pass
...
>>> class C[A]:
..     pass
...
>>> print A.x, B.x, C.x
1 1 1
1 được khởi tạo thành mặc định [i. e. , một danh sách trống] chỉ khi
>>> class A[object]:
..     x = 1
...
>>> class B[A]:
..     pass
...
>>> class C[A]:
..     pass
...
>>> print A.x, B.x, C.x
1 1 1
0 được xác định lần đầu tiên, nhưng sau đó gọi tới
>>> class A[object]:
..     x = 1
...
>>> class B[A]:
..     pass
...
>>> class C[A]:
..     pass
...
>>> print A.x, B.x, C.x
1 1 1
0 [i. e. , không có đối số
>>> class A[object]:
..     x = 1
...
>>> class B[A]:
..     pass
...
>>> class C[A]:
..     pass
...
>>> print A.x, B.x, C.x
1 1 1
1 được chỉ định] sẽ tiếp tục sử dụng cùng một danh sách mà
>>> class A[object]:
..     x = 1
...
>>> class B[A]:
..     pass
...
>>> class C[A]:
..     pass
...
>>> print A.x, B.x, C.x
1 1 1
1 đã được khởi tạo ban đầu

FYI, một cách giải quyết phổ biến cho việc này như sau

>>> def foo[bar=None]:
..    if bar is None:		# or if not bar:
..        bar = []
..    bar.append["baz"]
..    return bar
...
>>> foo[]
["baz"]
>>> foo[]
["baz"]
>>> foo[]
["baz"]

Sai lầm thường gặp #2. Sử dụng biến lớp không chính xác

Xem xét ví dụ sau

>>> class A[object]:
..     x = 1
...
>>> class B[A]:
..     pass
...
>>> class C[A]:
..     pass
...
>>> print A.x, B.x, C.x
1 1 1

Có ý nghĩa

>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1

Yup, một lần nữa như mong đợi

>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3

Cái gì $%#. &?? . Tại sao

>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1
5 cũng thay đổi?

Trong Python, các biến lớp được xử lý nội bộ dưới dạng từ điển và tuân theo thứ thường được gọi là Thứ tự giải quyết phương thức [MRO]. Vì vậy, trong đoạn mã trên, vì thuộc tính

>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1
6 không được tìm thấy trong lớp
>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1
7, nên nó sẽ được tra cứu trong các lớp cơ sở của nó [chỉ có
>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1
8 trong ví dụ trên, mặc dù Python hỗ trợ đa kế thừa]. Nói cách khác,
>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1
7 không có tài sản
>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1
6 riêng, độc lập với
>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1
8. Do đó, các tham chiếu đến
>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1
5 trên thực tế là các tham chiếu đến
>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1
4. Điều này gây ra sự cố Python trừ khi nó được xử lý đúng cách. Tìm hiểu thêm về thuộc tính lớp trong Python

Sai lầm thường gặp #3. Chỉ định tham số không chính xác cho một khối ngoại lệ

Giả sử bạn có đoạn mã sau

>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except ValueError, IndexError:  # To catch both exceptions, right?
..     pass
...
Traceback [most recent call last]:
  File "", line 3, in 
IndexError: list index out of range

Vấn đề ở đây là câu lệnh

>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3
4 không có danh sách các trường hợp ngoại lệ được chỉ định theo cách này. Thay vào đó, trong Python 2. x, cú pháp
>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3
5 được sử dụng để liên kết ngoại lệ với tham số tùy chọn thứ hai được chỉ định [trong trường hợp này là
>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3
6], để sẵn sàng kiểm tra thêm. Kết quả là, trong đoạn mã trên, ngoại lệ
>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3
7 không bị phát hiện bởi câu lệnh
>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3
4;

Cách thích hợp để bắt nhiều ngoại lệ trong câu lệnh

>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3
4 là chỉ định tham số đầu tiên là a chứa tất cả các ngoại lệ sẽ bị bắt. Ngoài ra, để có tính di động tối đa, hãy sử dụng từ khóa
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except ValueError, IndexError:  # To catch both exceptions, right?
..     pass
...
Traceback [most recent call last]:
  File "", line 3, in 
IndexError: list index out of range
1, vì cú pháp đó được hỗ trợ bởi cả Python 2 và Python 3

>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>

Sai lầm thường gặp #4. Hiểu sai quy tắc phạm vi Python

Độ phân giải phạm vi Python dựa trên cái được gọi là quy tắc LEGB, viết tắt của Cục bộ, Kèm theo, Toàn cầu, Tích hợp. Có vẻ đủ đơn giản, phải không? . Hãy xem xét những điều sau đây

>>> x = 10
>>> def foo[]:
..     x += 1
..     print x
...
>>> foo[]
Traceback [most recent call last]:
  File "", line 1, in 
  File "", line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment

Vấn đề là gì?

Lỗi trên xảy ra bởi vì, khi bạn gán một biến trong một phạm vi, biến đó được Python tự động coi là cục bộ của phạm vi đó và che khuất mọi biến có tên tương tự trong bất kỳ phạm vi bên ngoài nào

Do đó, nhiều người ngạc nhiên khi nhận được một mã

>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except ValueError, IndexError:  # To catch both exceptions, right?
..     pass
...
Traceback [most recent call last]:
  File "", line 3, in 
IndexError: list index out of range
2 trong mã đang hoạt động trước đây khi nó được sửa đổi bằng cách thêm một câu lệnh gán vào đâu đó trong phần thân của hàm. [Bạn có thể đọc thêm về điều này. ]

Điều này đặc biệt phổ biến đối với các nhà phát triển khi sử dụng danh sách. Xem xét ví dụ sau

>>> lst = [1, 2, 3]
>>> def foo1[]:
..     lst.append[5]   # This works ok...
...
>>> foo1[]
>>> lst
[1, 2, 3, 5]

>>> lst = [1, 2, 3]
>>> def foo2[]:
..     lst += [5]      # .. but this bombs!
...
>>> foo2[]
Traceback [most recent call last]:
  File "", line 1, in 
  File "", line 2, in foo
UnboundLocalError: local variable 'lst' referenced before assignment

Huh?

Câu trả lời giống như trong bài toán ví dụ trước nhưng được thừa nhận là tinh tế hơn.

>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except ValueError, IndexError:  # To catch both exceptions, right?
..     pass
...
Traceback [most recent call last]:
  File "", line 3, in 
IndexError: list index out of range
4 không giao nhiệm vụ cho
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except ValueError, IndexError:  # To catch both exceptions, right?
..     pass
...
Traceback [most recent call last]:
  File "", line 3, in 
IndexError: list index out of range
6, trong khi
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except ValueError, IndexError:  # To catch both exceptions, right?
..     pass
...
Traceback [most recent call last]:
  File "", line 3, in 
IndexError: list index out of range
3 thì. Hãy nhớ rằng
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except ValueError, IndexError:  # To catch both exceptions, right?
..     pass
...
Traceback [most recent call last]:
  File "", line 3, in 
IndexError: list index out of range
8 thực sự chỉ là viết tắt của
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except ValueError, IndexError:  # To catch both exceptions, right?
..     pass
...
Traceback [most recent call last]:
  File "", line 3, in 
IndexError: list index out of range
9, chúng tôi thấy rằng chúng tôi đang cố gắng gán một giá trị cho
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except ValueError, IndexError:  # To catch both exceptions, right?
..     pass
...
Traceback [most recent call last]:
  File "", line 3, in 
IndexError: list index out of range
6 [do đó Python cho rằng nó nằm trong phạm vi cục bộ]. Tuy nhiên, giá trị chúng tôi muốn gán cho
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except ValueError, IndexError:  # To catch both exceptions, right?
..     pass
...
Traceback [most recent call last]:
  File "", line 3, in 
IndexError: list index out of range
6 dựa trên chính
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except ValueError, IndexError:  # To catch both exceptions, right?
..     pass
...
Traceback [most recent call last]:
  File "", line 3, in 
IndexError: list index out of range
6 [một lần nữa, hiện được cho là thuộc phạm vi cục bộ], giá trị này vẫn chưa được xác định. bùng nổ

Sai lầm thường gặp #5. Sửa đổi một danh sách trong khi lặp lại nó

Vấn đề với đoạn mã sau khá rõ ràng

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
0

Xóa một mục khỏi danh sách hoặc mảng trong khi lặp lại nó là một vấn đề Python mà bất kỳ nhà phát triển phần mềm có kinh nghiệm nào cũng biết. Nhưng trong khi ví dụ trên có thể khá rõ ràng, ngay cả những nhà phát triển tiên tiến cũng có thể vô tình bị cắn bởi mã này phức tạp hơn nhiều

May mắn thay, Python kết hợp một số mô hình lập trình tao nhã, khi được sử dụng đúng cách, có thể dẫn đến mã được đơn giản hóa và hợp lý hóa đáng kể. Một lợi ích phụ của điều này là mã đơn giản hơn ít có khả năng bị cắn bởi lỗi vô tình xóa danh sách mục trong khi lặp đi lặp lại qua nó. Một mô hình như vậy là của. Hơn nữa, việc hiểu danh sách đặc biệt hữu ích để tránh vấn đề cụ thể này, như được thể hiện bằng cách triển khai thay thế mã trên hoạt động hoàn hảo này

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
1

Sai lầm thường gặp #6. Nhầm lẫn cách Python liên kết các biến trong bao đóng

Xét ví dụ sau

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
2

Bạn có thể mong đợi đầu ra sau

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
3

Nhưng bạn thực sự nhận được

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
4

Sự ngạc nhiên

Điều này xảy ra do hành vi ràng buộc muộn của Python nói rằng giá trị của các biến được sử dụng trong bao đóng được tra cứu tại thời điểm hàm bên trong được gọi. Vì vậy, trong đoạn mã trên, bất cứ khi nào bất kỳ hàm trả về nào được gọi, giá trị của

>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
3 sẽ được tra cứu trong phạm vi xung quanh tại thời điểm nó được gọi [và sau đó, vòng lặp đã hoàn thành, vì vậy ____________3 đã được gán cuối cùng của nó

Giải pháp cho vấn đề Python phổ biến này hơi phức tạp một chút

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
5

thì đấy. Chúng tôi đang tận dụng các đối số mặc định ở đây để tạo các hàm ẩn danh nhằm đạt được hành vi mong muốn. Một số sẽ gọi đây là thanh lịch. Một số sẽ gọi nó là tinh tế. Một số ghét nó. Nhưng nếu bạn là nhà phát triển Python, điều quan trọng là phải hiểu trong mọi trường hợp

Sai lầm thường gặp #7. Tạo các phụ thuộc mô-đun tròn

Giả sử bạn có hai tệp,

>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
5 và
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
6, mỗi tệp này nhập tệp kia, như sau

Trong

>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
5

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
6

Và trong

>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
6

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
7

Trước tiên, hãy thử nhập

>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
5

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
8

làm việc tốt. Có lẽ điều đó làm bạn ngạc nhiên. Rốt cuộc, chúng tôi có một lần nhập vòng tròn ở đây, điều này có lẽ là một vấn đề, phải không?

Câu trả lời là bản thân sự hiện diện của một lần nhập vòng tròn không phải là một vấn đề trong Python. Nếu một mô-đun đã được nhập, Python đủ thông minh để không cố nhập lại nó. Tuy nhiên, tùy thuộc vào thời điểm mà mỗi mô-đun đang cố gắng truy cập các hàm hoặc biến được xác định trong mô-đun kia, bạn thực sự có thể gặp sự cố

Vì vậy, quay trở lại ví dụ của chúng tôi, khi chúng tôi nhập

>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
5, không có vấn đề gì khi nhập
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
6, vì
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
6 không yêu cầu bất kỳ thứ gì từ
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
5 được xác định tại thời điểm nó được nhập. Tham chiếu duy nhất trong
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
6 đến
>>> x = 10
>>> def foo[]:
..     x += 1
..     print x
...
>>> foo[]
Traceback [most recent call last]:
  File "", line 1, in 
  File "", line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment
5 là cuộc gọi tới
>>> x = 10
>>> def foo[]:
..     x += 1
..     print x
...
>>> foo[]
Traceback [most recent call last]:
  File "", line 1, in 
  File "", line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment
6. Nhưng cuộc gọi đó là trong
>>> x = 10
>>> def foo[]:
..     x += 1
..     print x
...
>>> foo[]
Traceback [most recent call last]:
  File "", line 1, in 
  File "", line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment
7 và không có gì trong
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
5 hoặc
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
6 gọi
>>> x = 10
>>> def foo[]:
..     x += 1
..     print x
...
>>> foo[]
Traceback [most recent call last]:
  File "", line 1, in 
  File "", line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment
7. Vì vậy, cuộc sống là tốt

Nhưng điều gì sẽ xảy ra nếu chúng tôi cố gắng nhập

>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
6 [nghĩa là trước đó chưa nhập
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
5]

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
9

Uh-oh. Điều đó không tốt. Vấn đề ở đây là, trong quá trình nhập

>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
6, nó cố gắng nhập
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
5, đến lượt nó gọi
>>> lst = [1, 2, 3]
>>> def foo1[]:
..     lst.append[5]   # This works ok...
...
>>> foo1[]
>>> lst
[1, 2, 3, 5]

>>> lst = [1, 2, 3]
>>> def foo2[]:
..     lst += [5]      # .. but this bombs!
...
>>> foo2[]
Traceback [most recent call last]:
  File "", line 1, in 
  File "", line 2, in foo
UnboundLocalError: local variable 'lst' referenced before assignment
5, nó cố gắng truy cập
>>> lst = [1, 2, 3]
>>> def foo1[]:
..     lst.append[5]   # This works ok...
...
>>> foo1[]
>>> lst
[1, 2, 3, 5]

>>> lst = [1, 2, 3]
>>> def foo2[]:
..     lst += [5]      # .. but this bombs!
...
>>> foo2[]
Traceback [most recent call last]:
  File "", line 1, in 
  File "", line 2, in foo
UnboundLocalError: local variable 'lst' referenced before assignment
6. Nhưng
>>> lst = [1, 2, 3]
>>> def foo1[]:
..     lst.append[5]   # This works ok...
...
>>> foo1[]
>>> lst
[1, 2, 3, 5]

>>> lst = [1, 2, 3]
>>> def foo2[]:
..     lst += [5]      # .. but this bombs!
...
>>> foo2[]
Traceback [most recent call last]:
  File "", line 1, in 
  File "", line 2, in foo
UnboundLocalError: local variable 'lst' referenced before assignment
6 vẫn chưa được xác định. Do đó, ngoại lệ
>>> lst = [1, 2, 3]
>>> def foo1[]:
..     lst.append[5]   # This works ok...
...
>>> foo1[]
>>> lst
[1, 2, 3, 5]

>>> lst = [1, 2, 3]
>>> def foo2[]:
..     lst += [5]      # .. but this bombs!
...
>>> foo2[]
Traceback [most recent call last]:
  File "", line 1, in 
  File "", line 2, in foo
UnboundLocalError: local variable 'lst' referenced before assignment
8

Ít nhất một giải pháp cho điều này là khá tầm thường. Chỉ cần sửa đổi

>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
6 để nhập
>>> try:
..     l = ["a", "b"]
..     int[l[2]]
.. except [ValueError, IndexError] as e:  
..     pass
...
>>>
5 trong vòng
>>> x = 10
>>> def foo[]:
..     x += 1
..     print x
...
>>> foo[]
Traceback [most recent call last]:
  File "", line 1, in 
  File "", line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment
7

>>> def foo[bar=None]:
..    if bar is None:		# or if not bar:
..        bar = []
..    bar.append["baz"]
..    return bar
...
>>> foo[]
["baz"]
>>> foo[]
["baz"]
>>> foo[]
["baz"]
0

Không khi chúng tôi nhập nó, mọi thứ đều ổn

>>> def foo[bar=None]:
..    if bar is None:		# or if not bar:
..        bar = []
..    bar.append["baz"]
..    return bar
...
>>> foo[]
["baz"]
>>> foo[]
["baz"]
>>> foo[]
["baz"]
1

Sai lầm thường gặp #8. Xung đột tên với các mô-đun Thư viện chuẩn Python

Một trong những nét đẹp của Python là sự phong phú của các mô-đun thư viện mà nó đi kèm “có sẵn”. Nhưng kết quả là, nếu bạn không cố ý tránh nó, sẽ không khó để gặp phải xung đột tên giữa tên của một trong các mô-đun của bạn và một mô-đun có cùng tên trong thư viện chuẩn đi kèm với Python [ví dụ:

Điều này có thể dẫn đến các sự cố nghiêm trọng, chẳng hạn như nhập một thư viện khác, thư viện này lại cố gắng nhập phiên bản Thư viện chuẩn Python của một mô-đun, nhưng vì bạn có một mô-đun có cùng tên nên gói khác nhập nhầm phiên bản của bạn thay vì phiên bản bên trong . Đây là nơi xảy ra lỗi Python xấu

Do đó, cần cẩn thận để tránh sử dụng cùng tên với tên trong các mô-đun Thư viện chuẩn Python. Bạn có thể dễ dàng thay đổi tên của mô-đun trong gói của mình hơn là gửi Đề xuất cải tiến Python [PEP] để yêu cầu thay đổi tên ngược dòng và thử và được chấp thuận

Sai lầm thường gặp #9. Không giải quyết được sự khác biệt giữa Python 2 và Python 3

Xem xét tệp sau

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
03

>>> def foo[bar=None]:
..    if bar is None:		# or if not bar:
..        bar = []
..    bar.append["baz"]
..    return bar
...
>>> foo[]
["baz"]
>>> foo[]
["baz"]
>>> foo[]
["baz"]
2

Trên Python 2, điều này chạy tốt

>>> def foo[bar=None]:
..    if bar is None:		# or if not bar:
..        bar = []
..    bar.append["baz"]
..    return bar
...
>>> foo[]
["baz"]
>>> foo[]
["baz"]
>>> foo[]
["baz"]
3

Nhưng bây giờ, hãy thử sức với Python 3

>>> def foo[bar=None]:
..    if bar is None:		# or if not bar:
..        bar = []
..    bar.append["baz"]
..    return bar
...
>>> foo[]
["baz"]
>>> foo[]
["baz"]
>>> foo[]
["baz"]
4

Điều gì vừa xảy ra ở đây? . [Lý do cho điều này là, nếu không, nó sẽ giữ một chu kỳ tham chiếu với khung ngăn xếp trong bộ nhớ cho đến khi trình thu gom rác chạy và xóa các tham chiếu khỏi bộ nhớ. Thêm chi tiết kỹ thuật về điều này có sẵn]

Một cách để tránh vấn đề này là duy trì một tham chiếu đến đối tượng ngoại lệ bên ngoài phạm vi của khối

>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3
4 để nó vẫn có thể truy cập được. Đây là phiên bản của ví dụ trước sử dụng kỹ thuật này, do đó tạo ra mã thân thiện với cả Python 2 và Python 3

>>> def foo[bar=None]:
..    if bar is None:		# or if not bar:
..        bar = []
..    bar.append["baz"]
..    return bar
...
>>> foo[]
["baz"]
>>> foo[]
["baz"]
>>> foo[]
["baz"]
5

Chạy cái này trên Py3k

>>> def foo[bar=None]:
..    if bar is None:		# or if not bar:
..        bar = []
..    bar.append["baz"]
..    return bar
...
>>> foo[]
["baz"]
>>> foo[]
["baz"]
>>> foo[]
["baz"]
6

dippee

[Nhân tiện, chúng tôi thảo luận về một số điểm khác biệt quan trọng khác cần lưu ý khi di chuyển mã từ Python 2 sang Python 3. ]

Sai lầm thường gặp #10. Lạm dụng phương pháp
>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
06

Giả sử bạn có cái này trong một tệp có tên là

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
07

>>> def foo[bar=None]:
..    if bar is None:		# or if not bar:
..        bar = []
..    bar.append["baz"]
..    return bar
...
>>> foo[]
["baz"]
>>> foo[]
["baz"]
>>> foo[]
["baz"]
7

Và sau đó bạn đã cố gắng làm điều này từ

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
08

>>> def foo[bar=None]:
..    if bar is None:		# or if not bar:
..        bar = []
..    bar.append["baz"]
..    return bar
...
>>> foo[]
["baz"]
>>> foo[]
["baz"]
>>> foo[]
["baz"]
8

Bạn sẽ nhận được một ngoại lệ

>>> lst = [1, 2, 3]
>>> def foo1[]:
..     lst.append[5]   # This works ok...
...
>>> foo1[]
>>> lst
[1, 2, 3, 5]

>>> lst = [1, 2, 3]
>>> def foo2[]:
..     lst += [5]      # .. but this bombs!
...
>>> foo2[]
Traceback [most recent call last]:
  File "", line 1, in 
  File "", line 2, in foo
UnboundLocalError: local variable 'lst' referenced before assignment
8 xấu xí

Tại sao? . Kết quả là, trong ví dụ trên, tại thời điểm được gọi, tên

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
12 đã được đặt thành
>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
10

Một giải pháp cho vấn đề lập trình Python nâng cao hơn một chút này là sử dụng

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
14 thay thế. Theo cách đó, khi chương trình của bạn thực thi xong [tức là khi thoát bình thường], các trình xử lý đã đăng ký của bạn sẽ được khởi động trước khi trình thông dịch bị tắt

Với sự hiểu biết đó, một bản sửa lỗi cho mã

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
07 ở trên có thể giống như thế này

>>> def foo[bar=None]:
..    if bar is None:		# or if not bar:
..        bar = []
..    bar.append["baz"]
..    return bar
...
>>> foo[]
["baz"]
>>> foo[]
["baz"]
>>> foo[]
["baz"]
9

Việc triển khai này cung cấp một cách rõ ràng và đáng tin cậy để gọi bất kỳ chức năng dọn dẹp cần thiết nào khi chấm dứt chương trình thông thường. Rõ ràng, việc quyết định phải làm gì với đối tượng gắn với cái tên

>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
17 là tùy thuộc vào
>>> foo[]
["baz"]
>>> foo[]
["baz", "baz"]
>>> foo[]
["baz", "baz", "baz"]
16, nhưng bạn hiểu rồi đấy.

Gói [lại

Python là một ngôn ngữ mạnh mẽ và linh hoạt với nhiều cơ chế và mô hình có thể cải thiện đáng kể năng suất. Tuy nhiên, như với bất kỳ công cụ phần mềm hoặc ngôn ngữ nào, việc hiểu biết hoặc đánh giá hạn chế về các khả năng của nó đôi khi có thể là một trở ngại hơn là lợi ích, khiến một người rơi vào tình trạng “biết đủ là nguy hiểm”

Tự làm quen với các sắc thái chính của Python, chẳng hạn như [nhưng không giới hạn ở] các vấn đề lập trình nâng cao vừa phải được nêu trong bài viết này, sẽ giúp tối ưu hóa việc sử dụng ngôn ngữ trong khi tránh một số lỗi phổ biến hơn của nó

Bạn cũng có thể muốn xem các đề xuất của chúng tôi về các câu hỏi phỏng vấn có thể giúp xác định các chuyên gia Python

Python thuộc loại ngôn ngữ nào?

Python là ngôn ngữ lập trình hướng đối tượng, tương tác, thông dịch . Nó kết hợp các mô-đun, ngoại lệ, gõ động, kiểu dữ liệu động mức rất cao và các lớp. Nó hỗ trợ nhiều mô hình lập trình ngoài lập trình hướng đối tượng, chẳng hạn như lập trình thủ tục và chức năng.

Python có cao không

Python là ngôn ngữ lập trình cấp cao thông dịch, hướng đối tượng với ngữ nghĩa động.

Python có thể thấp không

Python và C# là những ví dụ về ngôn ngữ cấp cao được sử dụng rộng rãi trong giáo dục và tại nơi làm việc. Ngôn ngữ cấp cao là ngôn ngữ hướng tới người dùng ở chỗ nó được thiết kế để người lập trình dễ dàng chuyển đổi thuật toán thành mã chương trình. Ngôn ngữ cấp thấp là ngôn ngữ hướng máy.

Python khác với các ngôn ngữ lập trình khác như thế nào?

Python là ngôn ngữ được biên dịch và nhập động, trong khi Java là ngôn ngữ được biên dịch và nhập tĩnh . Mã Python không cần biên dịch trước khi chạy. Mặt khác, mã Java cần được biên dịch từ mã mà con người có thể đọc được thành mã mà máy có thể đọc được.

Chủ Đề