Giới hạn trên trong Python

Hàm range() trả về tạo ra một dãy số, bắt đầu từ cận dưới đến cận trên


range(lower_bound, upper_bound, step_size)


  • chặn dưới. Giá trị bắt đầu của danh sách

  • giới hạn trên. Giá trị tối đa của danh sách, không bao gồm số này

  • step_bound. Kích thước bước, sự khác biệt giữa mỗi số trong danh sách


Các tham số Lower_bound và step_size là tùy chọn. Theo mặc định, giới hạn dưới được đặt thành 0, bước tăng dần được đặt thành một. Các tham số phải thuộc loại số nguyên, nhưng có thể là số âm

Giới hạn trên trong Python
Hàm phạm vi python trong trình thông dịch

Khóa học liên quan.
Trại đào tạo lập trình Python. Đi từ con số 0 đến người hùng

sự khác biệt trong phạm vi triển khai
Sự khác biệt này thường không phải là vấn đề. Phạm vi () được triển khai hơi khác trong các phiên bản Python.


  • Trăn 2. x. Hàm range() trả về một danh sách

  • Trăn 3. x. Hàm range() tạo ra một chuỗi


phạm vi trong python 2. 7
Một cuộc gọi đến phạm vi (5) sẽ trở lại. 0,1,2,3,4.

>>> range(5)

Một cuộc gọi đến phạm vi (1,10) trả về. 1,2,3,4,5,6,7,8,9


>>> range(1,10)

Phạm vi gọi (0,10,2) trả về. 0,2,4,6,8


>>> range(0,10,2)

dải ô trong python 3
Để tạo danh sách sử dụng dải ô, hãy thêm hàm danh sách


>>> list(range(5))

Chúng ta có thể sử dụng tất cả các tham số (cận dưới, cận trên, bước)


>>> list(range(0,10,2))
[0, 2, 4, 6, 8]

triển khai python 2
Phiên bản này của phạm vi () phân bổ bộ nhớ máy tính và cũng điền vào bộ nhớ máy tính đằng sau hậu trường. Đối với phạm vi lớn, việc triển khai này không hiệu quả lắm.

Thông thường, bạn sẽ không gặp bất kỳ vấn đề nào với việc triển khai phạm vi () trong Python2 nhưng nếu bạn sử dụng số lượng lớn (hàng triệu mục), bạn có thể gặp sự cố

Các ràng buộc về phiên bản giới hạn (viết hoa trên) đang bắt đầu xuất hiện trong hệ sinh thái Python. Điều này đang gây ra các vấn đề trong thế giới thực với các thư viện tuân theo đề xuất này và có khả năng; . Trong cuộc thảo luận này, tôi muốn giải thích lý do tại sao việc luôn cung cấp giới hạn trên lại gây hại nhiều hơn là có lợi ngay cả đối với các thư viện SemVer thực sự, tại sao các thư viện xác định giới hạn trên lại yêu cầu cập nhật thường xuyên hơn thay vì ít hơn và tại sao nó không thể mở rộng. Sau khi đọc phần này, hy vọng bạn sẽ luôn cân nhắc mọi giới hạn bạn thêm vào, bạn sẽ biết (một vài) vị trí mà việc ghim giới hạn trên là hợp lý và thậm chí có thể tránh sử dụng các thư viện ghim giới hạn trên một cách không cần thiết cho đến khi tác giả cập nhật chúng để loại bỏ những giới hạn này

Nếu con số khổng lồ 10.000 từ này hơi dài đối với bạn, thì hãy bỏ qua việc sử dụng mục lục hoặc xem phần ở cuối hoặc đọc số phiên bản của Bernát Gábor, ngắn hơn nhưng là một bài đọc tuyệt vời với các ví dụ hay và dễ thương . Hoặc phiên bản ngữ nghĩa của Hynek sẽ không cứu bạn Hãy chắc chắn kiểm tra ít nhất trước khi bạn rời đi

Cũng được cảnh báo, tôi chọn Thơ khá nhiều. Sự phổ biến ngày càng tăng của Thơ có thể là do sự đơn giản của việc có một công cụ so với. nhiều thứ để đóng gói, nhưng nó cũng có một bộ giải phụ thuộc đặc biệt, một cú pháp giới hạn trên mới và một - đối lập trực tiếp với các thành viên của nhóm nhà phát triển cốt lõi Python và các nhà phát triển PyPA. Không phải tất cả các thư viện có giới hạn phiên bản quá mức đều là các dự án Thơ (như TensorFlow), nhưng rất nhiều trong số đó là. Nói rõ hơn, Poetry không ép buộc bạn phải ghim phiên bản, nhưng nó thúc đẩy bạn thực sự, rất khó để luôn giới hạn phiên bản và nó nhắm mục tiêu đến những người dùng Python mới chưa biết cách nào tốt hơn là chấp nhận các đề xuất tồi. Và những điều này ảnh hưởng đến toàn bộ hệ sinh thái, bao gồm cả những người dùng không sử dụng thơ, nhưng muốn phụ thuộc vào các thư viện sử dụng. Tôi thực sự thích các khía cạnh khác của Thơ và cuối cùng muốn giúp nó xây dựng các gói nhị phân với Scikit-build (CMake) thông qua một plugin và tôi vui vẻ sử dụng nó trong một số dự án của mình. Nếu tôi không chọn đủ Thơ cho bạn, đừng lo lắng, tôi có một bài đăng tiếp theo sẽ chọn nó chi tiết hơn nhiều. Ngoài ra, hãy xem pdm, mang lại nhiều lợi ích của Thơ khi tuân theo các tiêu chuẩn PEP

Điều này hóa ra khá dài (và thậm chí còn lâu hơn sau khi được các nhà phát triển cốt lõi của PyPA và Python đánh giá), vì vậy tôi đã bao gồm một mục lục. Thoải mái chuyển sang nội dung mà bạn quan tâm. Điều này cũng được chia thành ba bài viết, đầu tiên là ứng dụng so với. thư viện, và cuối cùng là phiên bản Thơ

Mục lục (bấm vào để mở rộng)
giới thiệu

Giới hạn phiên bản là gì?

click>=7

bạn viết cái này

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0

Hoặc, chỉ trong Thơ, điều này


>>> range(5)

1

Điều này cho phép mọi phiên bản mới hơn nhưng không bao gồm phiên bản “chính” tiếp theo. Cú pháp ở đây được điều chỉnh bởi PEP 440, ngoại trừ phần bổ sung của Thơ, xuất phát từ các ngôn ngữ khác như npm của JavaScript

học kỳ

Hãy định nghĩa ngắn gọn về SemVer - cả SemVer thực và SemVer thực tế. Một điểm khác biệt giữa bài đăng này và những nỗ lực trước đó của những người khác là tôi thậm chí sẽ giải quyết các phụ thuộc sử dụng SemVer thực sự (phải thừa nhận rằng không có bản phát hành bản vá nhỏ và bản vá nào là một khái niệm không thể đạt được)

SemVer cho biết có ba chữ số phiên bản ở dạng

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
4. “Quy tắc” là chỉ cho phép sửa chữa nếu số lượng bản vá tăng lên, chỉ cho phép bổ sung nếu phiên bản phụ bị lỗi và nếu bạn làm bất cứ điều gì có thể gây ảnh hưởng đến người dùng ở hạ lưu, thì phiên bản chính phải được cập nhật. Tôi giới thiệu bài viết xuất sắc ở đây của người bảo trì tox/virtualenv và thành viên PyPA đồng nghiệp, Bernát Gábor

Bất cứ khi nào có bất kỳ mã hạ nguồn nào bị hỏng và đó không phải là bản phát hành chính, thì bạn sẽ có một nhóm người ngay lập tức bắt đầu phàn nàn rằng thư viện “không tuân theo SemVer”, và đó không phải là vấn đề với SemVer, mà là vấn đề của bạn . Có thể tìm thấy một cuộc thảo luận dài ở đây, nhưng tôi sẽ nói sơ qua về nó. Phiên bản nào bạn gặp sự cố khi cập nhật? . Về cơ bản, một thư viện SemVer “hoàn hảo” gần như luôn cập nhật phiên bản chính, vì bạn hầu như luôn có thể phá vỡ người dùng bằng bất kỳ thay đổi nào (và nếu bạn có đủ người dùng, theo luật của Hyrum, bạn sẽ làm điều này). Điều này làm cho SemVer “đúng” trở nên vô nghĩa. Phát hành nhỏ là không thể, và phát hành bản vá là gần như không thể. Nếu bạn sửa một lỗi, ai đó có thể tùy thuộc vào hành vi của lỗi đó (distutils, đang nhìn bạn). Tất nhiên, ngay cả một người theo chủ nghĩa thuần túy SemVer cũng sẽ thừa nhận rằng người dùng không nên vi phạm nếu bạn thêm hoặc sửa một thứ gì đó, nhưng điều đó không có nghĩa là không có thứ gọi là SemVer “thuần túy”

Giới hạn trên trong Python

Ví dụ. phân tích cú pháp 3. 0. 5 (bấm để mở rộng)

PyParsing gần đây đã xóa một thuộc tính không công khai trong

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
5, điều này đã phá vỡ tất cả các phiên bản đóng gói, vì nó tình cờ sử dụng thuộc tính không riêng tư này. Đây có phải là lỗi của bao bì này không? . 0. 5 là một bản phát hành đột phá cho rất nhiều hệ sinh thái Python. bao bì 21. 2 đã được phát hành với giới hạn (giới hạn sai,
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
6 thay vì
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
7, vì vậy nó phá vỡ các thư viện mong đợi
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
8 đã hoạt động trước đó). phân tích cú pháp 3. 0. 6 đã khôi phục thuộc tính riêng tư này và bao bì cũng ngừng sử dụng nó

Việc bỏ Python 2 có yêu cầu phát hành chính không? . )

Bây giờ, đừng hiểu sai ý tôi, tôi thích SemVer “thực tế” hoặc “gần như” - Cá nhân tôi sử dụng nó trên tất cả các thư viện của mình (tôi không duy trì một thư viện CalVer nào như pip hoặc thư viện chỉ trực tiếp tại đầu, . SemVer thực tế chủ yếu tuân theo quy tắc trên, nhưng thừa nhận thực tế là nó không hoàn hảo. Nó cũng thường thêm một quy tắc mới vào hỗn hợp. nếu bạn không dùng một tính năng (hầu như luôn có trong một bản phát hành nhỏ), bạn có thể xóa tính năng đó trong một bản phát hành nhỏ trong tương lai. Bạn phải kiểm tra thư viện để xem thời gian ngừng sử dụng là bao lâu - NumPy và Python sử dụng ba bản phát hành nhỏ. Điều này cũng được sử dụng trong CalVer (lập phiên bản dựa trên ngày tháng) - bạn có thể đặt thời gian ngừng sử dụng theo thời gian. Các thư viện thực sự lớn ghét tạo ra các bản phát hành lớn - Python 2->3 là một thảm họa. Những người theo chủ nghĩa thuần túy SemVer lập luận rằng điều này biến các bản phát hành nhỏ thành bản phát hành chính, nhưng nó không đơn giản như vậy - thời gian ngừng sử dụng đảm bảo phiên bản “tiếp theo” hoạt động, điều này thực sự hữu ích và thường cho bạn thời gian để điều chỉnh trước khi quá trình xóa xảy ra. Đó là một sự cân bằng tuyệt vời cho các dự án được duy trì tốt bằng cách sử dụng các thư viện phát triển với tốc độ hợp lý. Nếu bạn chắc chắn rằng bạn có thể thấy các phiên bản không dùng nữa, thì hầu như bạn sẽ luôn làm việc với một số phiên bản tiếp theo

Để tránh sự nhầm lẫn có thể xảy ra trong tương lai, một số thư viện do dự về việc phát hành các phiên bản chính mới (như Python, NumPy và CMake) đến mức họ thực hiện các thay đổi đột phá (như xóa các tính năng không dùng nữa) trên các bản phát hành nhỏ, giúp thúc đẩy các phiên bản phụ lên các phiên bản chính theo . Đây là kỳ vọng xã hội trong hệ sinh thái và là một lý do khiến một số thư viện sử dụng CalVer

Mô tả tốt nhất về SemVer thực tế mà tôi từng thấy là nó là một “bản ghi thay đổi viết tắt”. Tôi thích điều này, vì tôi thích nhật ký thay đổi - tôi nghĩ đó là phần quan trọng nhất trong tài liệu mà bạn có, nhật ký thay đổi được viết tốt cho phép bạn xem những gì đã bị thiếu trước đây để bạn biết không tìm kiếm nó trong các phiên bản cũ hơn, nó cho bạn biết những gì . Với SemVer, bạn có thể xem phiên bản và điều đó cho bạn ý tưởng nhanh về mức độ lớn và loại thay đổi nào đã xảy ra trước khi kiểm tra nhật ký thay đổi

Người giải quyết

Chúng ta cần đề cập ngắn gọn về bộ giải, vì có một số bộ giải và một lý do khiến điều này ngày nay phù hợp hơn so với vài năm trước là do những thay đổi trong bộ giải

Bộ giải Pip đã thay đổi trong phiên bản 20. 3 để trở nên thông minh hơn đáng kể. Bộ giải cũ sẽ bỏ qua các yêu cầu bắc cầu không tương thích thường xuyên hơn nhiều so với bộ giải mới. Điều này có nghĩa là giới hạn trên trong thư viện có thể đã bị bỏ qua trước đó, nhưng có nhiều khả năng làm hỏng mọi thứ hoặc thay đổi lời giải ngay bây giờ

Nó cố gắng tìm một tập hợp các phụ thuộc đang hoạt động mà tất cả đều đồng ý với nhau. Bằng cách nhìn ngược thời gian, thật vui khi giải quyết các phiên bản gói rất cũ nếu các gói mới hơn được cho là không tương thích. Điều này có thể hữu ích, nhưng chậm và cũng có nghĩa là bạn có thể dễ dàng nhận được một bộ gói rất cũ khi bạn nghĩ rằng mình đang nhận được các phiên bản mới nhất

Thơ có một bộ giải độc đáo và rất nghiêm ngặt (và chậm hơn), thậm chí còn đi xa hơn để tìm kiếm các giải pháp. Nó buộc bạn phải giới hạn Python nếu một phụ thuộc không. Một điểm khác biệt chính là Thơ có đặc tả môi trường ban đầu để hoạt động mọi lúc, trong khi pip không biết các ràng buộc môi trường ban đầu là gì. Điều này cho phép Thơ khôi phục sự phụ thuộc vào giải pháp tiếp theo, trong khi pip không biết các yêu cầu ban đầu là gì và do đó không biết liệu gói cũ có hợp lệ khi gặp giới hạn mới hay không

Trình giải quyết của Conda giống như Thơ và do số lượng bản dựng trong các kênh conda, điều này sẽ khiến bạn sợ hãi - việc giải quyết ban đầu cho một môi trường có số lượng lớn các thành phần phụ thuộc (bao gồm một gói duy nhất như ROOT) có thể mất vài phút và các bản cập nhật cho các môi trường hiện có . Tôi chưa bao giờ làm Thơ mất hơn 70 giây, nhưng tôi cũng chưa sử dụng nó cho bất kỳ thứ gì lớn; . Conda đã trở nên tốt hơn bằng cách sử dụng nhiều phím tắt hơn và đoán mọi thứ (tôi đã không giải quyết được hơn 25 giờ trong một thời gian), và việc triển khai C của Mamba và các thuật toán tốt hơn thực sự hữu ích, nhưng thực hiện một giải pháp “thông minh” thì khó

Chúng ta sẽ thấy nó một lần nữa, nhưng chỉ để chỉ ra nó ở đây. lỗi bộ giải hoàn toàn là lỗi và không thể sửa được ở hạ lưu. Nếu một thư viện yêu cầu


>>> range(5)

10 và một thư viện khác yêu cầu

>>> range(5)

11, thế là xong, bạn ngừng kinh doanh. Bộ giải “thông minh” có thể tìm kiếm các phiên bản cũ hơn của các thư viện đó để xem liệu có tồn tại phiên bản nào không có giới hạn đó hay không - nếu phiên bản có giới hạn dưới cao có bản phát hành có giới hạn trên dưới, thì đó là những gì nó sẽ chọn; . kể từ khi phát hành. Chúng ta sẽ thảo luận về những vấn đề này gây ra sau. Tuy nhiên, một bản dựng không bị ràng buộc là hoàn toàn tầm thường để khắc phục đối với bất kỳ người dùng nào. Đó chỉ là một sự bất tiện nhỏ đối với một số lượng lớn người dùng

Vấn đề. Dựa vào SemVer để giới hạn các phiên bản

Bây giờ đến vấn đề. Nếu bạn có một phụ thuộc, bạn có nên thêm một giới hạn trên không? . Đảm bảo rằng bạn hiểu sự khác biệt giữa thư viện và ứng dụng, như đã định nghĩa trong bài viết trước

Chúng tôi sẽ đề cập đến các trường hợp sử dụng hợp lệ để giới hạn sau phần này. Tuy nhiên, để rõ ràng, nếu bạn biết bạn không hỗ trợ một bản phát hành mới của thư viện, thì tuyệt đối, hãy tiếp tục và giới hạn nó ngay khi bạn biết điều này là đúng. Nếu một cái gì đó không hoạt động, bạn nên giới hạn (hoặc có thể hạn chế một phiên bản nếu thư viện ngược dòng có lỗi tạm thời thay vì hướng thiết kế gây ra lỗi). Bạn cũng nên làm nhiều nhất có thể để nhanh chóng gỡ bỏ giới hạn, vì tất cả các nhược điểm của giới hạn trong các phần tiếp theo vẫn được áp dụng

Những điều sau đây sẽ cho rằng bạn đang giới hạn trước khi biết rằng một cái gì đó không hoạt động, nhưng chỉ nằm ngoài nguyên tắc chung, như Thơ khuyến nghị và mặc định với


>>> range(5)

12 và mẫu mặc định. Trong hầu hết các trường hợp, câu trả lời sẽ là “không”. Để đơn giản, tôi cũng sẽ cho rằng bạn đang muốn giới hạn các bản phát hành chính (

>>> range(5)

13 trong Thơ hoặc

>>> range(5)

14 trong tất cả các công cụ khác tuân theo tiêu chuẩn Python thông qua PEP 440). Nếu bạn giới hạn ở các phiên bản phụ (

>>> range(5)

15), điều này còn tồi tệ hơn nhiều và các lập luận bên dưới thậm chí còn áp dụng mạnh mẽ hơn

Phiên bản cũng giới hạn mã phá vỡ

Thư viện. ✅ (áp dụng)

Đăng kí. ✳️ (áp dụng một phần)

Không ai thích có một người dùng phá vỡ cập nhật. Ví dụ: IPython phụ thuộc vào thư viện có tên là Jedi. hiệp sĩ 0. 18 đã xóa thứ mà IPython đã sử dụng, vì vậy cho đến IPython 7. 20 đã được phát hành, một giải pháp "bình thường" (như


>>> range(5)

16) dẫn đến cài đặt bị hỏng. Thật hấp dẫn khi nhìn lại điều đó và nói "tốt, nếu IPython giới hạn các phụ thuộc của nó, thì điều đó đã có thể tránh được". Trên thực tế, mỗi khi điều này xảy ra, bạn sẽ thấy những đề xuất có ý nghĩa tốt nhưng sai lầm cho rằng đây là bằng chứng cho thấy lẽ ra bạn nên chọn giới hạn trên hợp lý cho tất cả các yêu cầu

Tuy nhiên, giới hạn các phụ thuộc cũng làm hỏng mọi thứ và bạn không thể sửa nó ở hạ lưu. Nếu tôi viết một thư viện hoặc ứng dụng phụ thuộc vào thư viện có phần phụ thuộc bị hỏng, tôi có thể giới hạn nó và sau đó người dùng của tôi hài lòng. Trong trường hợp trên, giải pháp tạm thời là chỉ ghim Jedi theo cách thủ công, chẳng hạn như


>>> range(5)

17 cho người dùng hoặc giới hạn nó trong phần phụ thuộc cho thư viện hoặc ứng dụng. Bất kỳ người dùng nào cũng có thể dễ dàng làm điều đó - khó chịu và trừu tượng bị rò rỉ, nhưng có thể sửa được. Nhưng bạn không thể khắc phục một ràng buộc quá mức - và đây không chỉ là vấn đề về pip; . Hầu hết các bản phát hành khác của Jedi đều ổn, việc giới hạn các phiên bản khác sẽ gây ra vấn đề đối với những người dùng không quan tâm hoặc biết về IPython khi sử dụng Jedi

Thực sự có một giải pháp cho vấn đề này bên ngoài các gói PyPI/pip; . Conda hỗ trợ điều này một cách tự nhiên;

Nếu bạn đặt giới hạn trên, điều này cũng không thể dễ dàng khắc phục bởi các phụ thuộc của bạn - nó thường buộc sửa lỗi trên thư viện thực hiện ghim. Điều này có nghĩa là mọi bản phát hành chính của mọi phần phụ thuộc mà bạn giới hạn ngay lập tức yêu cầu bạn tạo một bản phát hành mới hoặc mọi người sử dụng thư viện của bạn không thể sử dụng phiên bản mới nhất của các thư viện đó nữa. Nếu "tạo bản phát hành mới nhanh chóng" ở trên làm phiền bạn;

Điều đó cũng có nghĩa là bạn phải hỗ trợ nhiều phiên bản khác nhau; . Ví dụ: giả sử bạn ủng hộ


>>> range(5)

18. Nhấp chuột 8 xuất hiện và ai đó viết thư viện yêu cầu

>>> range(5)

19. Bây giờ thư viện của bạn không thể được cài đặt cùng lúc với thư viện khác, yêu cầu của bạn không trùng lặp. Nếu bạn cập nhật lên yêu cầu

>>> range(1,10)

80, thì bản cập nhật của bạn không thể được cài đặt với một thư viện khác vẫn còn trên

>>> range(5)

18. Vì vậy, bạn phải hỗ trợ

>>> range(1,10)

82 trong một thời gian cho đến khi hầu hết các thư viện được cập nhật tương tự (và điều này làm cho cú pháp

>>> range(1,10)

83 khá vô dụng, IMO). Ví dụ cụ thể này đặc biệt tệ, bởi vì a) nó phổ biến, b) nhấp chuột được sử dụng cho phần "ứng dụng" của mã, phần này thậm chí có thể không được sử dụng nếu bạn đang sử dụng nó làm thư viện và c)

Thư viện yêu cầu can thiệp phiên bản thủ công không bị hỏng, nó chỉ gây khó chịu. Thư viện không thể cài đặt do xung đột phiên bản bị hỏng. Nếu xung đột phiên bản đó là giả, thì bạn đã tạo ra một vấn đề không thể giải quyết được trong đó không tồn tại

Các bản sửa lỗi không phải lúc nào cũng được nhập lại

Thư viện. ✅ (áp dụng)

Đăng kí. ✅ (áp dụng)

Tôi sẽ chuyển sang bài đăng của Bernát để giải thích rằng nhiều dự án Python đơn giản là không có thời gian hoặc tài nguyên để tiếp tục cung cấp các bản sửa lỗi và bản vá bảo mật cho các phiên bản chính cũ. Tôi đã từng bị các dự án như pip và pandas từ chối yêu cầu cập nhật bản vá lỗi cũ. Trên thực tế, tôi thậm chí đã từ chối họ cho CLI11, CI của tôi đã bị hỏng trên 1 cái cũ. x nên tôi không thể chạy thử nghiệm

Hãy xem xét một ví dụ cụ thể. Giả sử một số thư viện phiên bản 6. 1. 0 đã hoạt động. Bạn ghim vào


>>> range(1,10)

85. Sau đó,

>>> range(1,10)

86 xuất hiện và phá mã của bạn. Sự cố đã được phát hiện và khắc phục, nhưng quá trình phát triển đã đi quá xa để có thể dễ dàng sao lưu hoặc quá phức tạp nên

>>> range(1,10)

87 hoạt động trở lại. Giới hạn của bạn hiện đã bị hỏng và mã của bạn không hoạt động mãi mãi (phần tiếp theo). Tôi đã thấy các bản sửa lỗi như thế này nhiều lần và cũng chịu trách nhiệm cho chúng. Thường thì hệ thống CI bị hỏng đối với các bản phát hành cũ, không rõ ràng và việc quay lại và sửa phiên bản cũ là không khả thi

Phiên bản mã mới hơn nhằm mục đích tốt hơn các phiên bản mã cũ hơn; . Bạn không bao giờ nên hạn chế khả năng cập nhật những thứ mà bạn phụ thuộc vào của người dùng (các phụ thuộc không nên là một bản tóm tắt dễ bị rò rỉ; người dùng không cần phải biết hoặc quan tâm đến những gì bạn phụ thuộc vào). Giới hạn trên hạn chế đáng kể khả năng cập nhật mà người dùng cuối không biết

Bạn muốn người dùng sử dụng các phiên bản mới nhất của mã của bạn. Bạn muốn có thể phát hành các bản sửa lỗi và yêu cầu người dùng nhận các bản sửa lỗi đó. Và, nói chung, bạn không muốn phải phát hành các phiên bản vá lỗi mới cho các phiên bản chính cũ (hoặc thậm chí là phụ) trong thư viện của mình, ít nhất là từ rất xa. Vậy tại sao lại buộc các phần phụ thuộc của bạn hỗ trợ các phiên bản cũ với các bản phát hành bản vá vì bạn đã giới hạn chúng?

Mã không hoạt động mãi mãi

Thư viện. ✅ (áp dụng)

Đăng kí. ✅ (áp dụng)

Một tuyên bố mà tôi đã thấy các nhà phát triển Thơ đưa ra là việc giới hạn các phụ thuộc của bạn có nghĩa là mã của bạn sẽ hoạt động trong tương lai. Hy vọng rằng tôi không phải nói với bạn điều này là sai; . Có rất nhiều lý do khiến mã bị hỏng mà không thay đổi nó, hãy xem xét một vài lý do

Một trong những cái gần đây nhất là quá trình chuyển đổi macOS Apple Silicon. Để có được mã làm việc, bạn phải có các phiên bản gói mới nhất. Trăn 3. 9 (và sau đó được nhập vào 3. 8) được yêu cầu trên Apple Silicon, vì vậy nếu bạn phụ thuộc vào thư viện giới hạn không hỗ trợ các phiên bản đó, thì bạn không gặp may, mã đó không hoạt động. Điều này cũng đúng với tất cả các thư viện lớn không hỗ trợ Apple Silicon quá xa. Bạn cần một NumPy, pip, bao bì, Thơ gần đây, v.v. Và đề phòng trường hợp bạn nghĩ rằng mình có nhiều thời gian, hãy lưu ý rằng Apple thậm chí không còn bán máy tính xách tay dựa trên Intel nữa, đúng một năm sau khi quá trình chuyển đổi bắt đầu

Các đợt triển khai hỗ trợ tương tự đã xảy ra (hoặc đang diễn ra) cho các kiến ​​trúc Linux và Windows (như ARM và PowerPC), các bản cập nhật hệ điều hành (hệ thống đánh số mới của macOS 11 đã phá vỡ pip, Thơ và nhiều thứ khác, cũng có thể xảy ra ở mức độ thấp hơn trên . Đơn giản là mã sẽ không bao giờ hoạt động mãi mãi và việc cho phép khả năng sử dụng các thư viện mới hơn sẽ tăng cơ hội sử dụng nó. Nếu bạn giới hạn NumPy ở mức 1. 17, bạn sẽ không bao giờ ủng hộ Apple Silicon. Tuy nhiên, nếu bạn không giới hạn nó và phiên bản cuối cùng mà bạn thực sự hỗ trợ sẽ là 1. 21, thì mã của bạn sẽ hoạt động với Apple Silicon và những người dùng trong tương lai có thể phải giới hạn các phiên bản theo cách thủ công ở mức <1. 22 cuối cùng, nhưng nó sẽ hoạt động

Các phiên bản giới hạn giả tạo sẽ luôn làm giảm khả năng nó hoạt động trong tương lai. Nó chỉ giúp người dùng trong tương lai không phải thêm các giới hạn bổ sung, nhưng đây là sự cố có cách giải quyết đơn giản cho người dùng và điều đó không có khả năng xảy ra đối với nhiều phụ thuộc. Nếu ai đó đang sử dụng phiên bản mã cũ nhiều năm của bạn, thì có thể bạn đã biến mất (và do đó bạn không thể sửa các giới hạn trên bị hỏng) hoặc họ buộc phải sử dụng phiên bản cũ, có thể là do ai đó đã ghim (một cách giả tạo)

Ví dụ về Nhân sư và Docutils

Điều này đã xảy ra gần đây với Sphinx & docutils. Vấn đề là docutils 0. 18 nhân sư đã phá vỡ. Dưới đây là các lý do hợp lệ để giới hạn, bạn sẽ thấy rằng docutils trong Sphinx thực sự có thể vượt qua ngưỡng để ghim. Tuy nhiên, điều trớ trêu ngọt ngào ở đây là hóa ra Sphinx đã bắt đầu giới hạn tài liệu trong Sphinx 4, nhưng những người dùng bị ảnh hưởng đã giới hạn Sphinx ở phiên bản 3

SemVer không bao giờ hứa sẽ phá mã của bạn

Thư viện. ✅ (áp dụng)

Đăng kí. ✅ (áp dụng)

Một cách khái quát hóa quy tắc SemVer thực sự dễ dàng nhưng không chính xác là “một phiên bản chính sẽ phá vỡ mã của tôi”. Đó là cơ sở để Thơ khuyến nghị luôn giới hạn các phiên bản, nhưng đó là một ngụy biện logic. Ngay cả khi thư viện tuân theo đúng SemVer một cách hoàn hảo, thì một phiên bản lớn không hứa hẹn sẽ phá vỡ mã hạ lưu. Nó hứa hẹn rằng một số mã hạ nguồn có thể bị hỏng. Ví dụ: nếu bạn sử dụng pytest để kiểm tra mã của mình, phiên bản chính tiếp theo sẽ rất khó bị hỏng. Tuy nhiên, nếu bạn viết một tiện ích mở rộng pytest, thì khả năng thứ gì đó bị hỏng sẽ cao hơn nhiều (nhưng không phải 100%, thậm chí có thể không phải 50%). Trớ trêu thay, một gói tuân theo SemVer càng tốt thì thay đổi sẽ kích hoạt một phiên bản chính càng nhỏ và do đó, càng ít khả năng một phiên bản chính sẽ phá vỡ một mã hạ nguồn cụ thể

Theo nguyên tắc chung, nếu bạn có một phụ thuộc ổn định hợp lý và bạn chỉ sử dụng API được ghi lại, đặc biệt nếu mức sử dụng của bạn khá nhẹ/chung chung, thì một bản cập nhật lớn rất khó có thể phá vỡ mã của bạn. Rất hiếm khi việc sử dụng nhẹ thư viện bị hỏng trong một bản cập nhật lớn. Nó có thể xảy ra, tất nhiên, nhưng không chắc. Nếu bạn đang sử dụng một thứ gì đó nặng nề, nếu bạn đang làm việc trên một tiện ích mở rộng khung hoặc nếu bạn sử dụng các phần nội bộ không được ghi lại công khai, thì khả năng bạn vi phạm bản phát hành chính sẽ cao hơn nhiều. Như đã đề cập trước đây, Python có văn hóa sản xuất


>>> range(1,10)

88,

>>> range(1,10)

89 hoặc

>>> range(0,10,2)

80 (hãy đảm bảo rằng chúng được bật trong thử nghiệm của bạn và biến thành lỗi), các thư viện tốt sẽ sử dụng chúng

Nghe có vẻ nực cười, nhưng có lẽ tôi nên chỉ ra rằng các thư viện CalVer không tuân theo SemVer (thông thường).


>>> range(0,10,2)

81 vẫn sẽ làm

>>> range(0,10,2)

82 cho phiên bản mà nó thêm vào. Bạn không nên giới hạn các phiên bản, nhưng bạn thực sự không nên giới hạn CalVer. phụ thuộc vào

>>> range(0,10,2)

83 tại thời điểm viết (mặc dù nó thực sự cung cấp nó). Trên thực tế, nếu tôi có thể tìm thấy nơi Thơ (không phải thơ-lõi, nhà cung cấp nó) sử dụng bao bì, tôi sẽ biết cách kích hoạt lỗi do nắp trên bao bì. Nhưng tôi không thể tìm thấy nó được nhập ở đâu ngoài trong các bài kiểm tra

Các phiên bản vá lỗi cũng có thể bị hỏng

Nếu bạn cho phép các phiên bản vá mới, bạn vẫn có thể bị hỏng, đặc biệt là do các lỗi bảo mật được nhận thấy. Một ví dụ gần đây là Python 3. 7. 14, 3. 8. 14, 3. 9. 14 và 3. 10. 7, tất cả đã sửa lỗi CVE liên quan đến từ chối dịch vụ với việc chuyển đổi số nguyên lớn thành chuỗi. Điều này đã phá vỡ một số người dùng, như Sage; . Đơn giản là bạn không thể dự đoán tương lai. (Và, trong trường hợp này, có lẽ bạn cũng không thể kiểm soát phiên bản vá lỗi mà người dùng Python sử dụng.

Và nếu bạn giới hạn các phiên bản vá lỗi, bạn sẽ không nhận được các bản sửa lỗi bảo mật. Khi một lỗ hổng được vá, phát hành và công khai, tất cả những gì ai đó phải làm là tìm một thư viện khắc phục lỗ hổng đó, sau đó nhắm mục tiêu người dùng có lỗ hổng đó. Đây là lý do GitHub cung cấp các thẻ di chuyển cho các Tác vụ GitHub;

Nó không quy mô

Thư viện. ✅ (áp dụng)

Đăng kí. ❌ (không áp dụng)

Nếu bạn có một thư viện duy nhất chơi không tốt, thì có thể bạn sẽ dễ dàng giải quyết được - đây là một lý do mà phương pháp này thoạt đầu có vẻ không tệ lắm. Tuy nhiên, nếu nhiều gói bắt đầu tuân theo giới hạn chặt chẽ này, thì bạn sẽ gặp phải một tình huống mà mọi thứ đơn giản là không thể giải quyết được - một ứng dụng có kích thước vừa phải có thể có hàng trăm phụ thuộc trở lên khi được mở rộng. Toàn bộ điểm đóng gói là cho phép bạn nhận được nhiều gói mà mỗi gói thực hiện một số công việc cho bạn - chúng tôi nên cố gắng làm cho việc thêm các phụ thuộc trở nên dễ dàng hơn chứ không khó hơn

Hàm ý của điều này là bạn nên rất cẩn thận khi thấy các yêu cầu chặt chẽ trong các gói và bạn có bất kỳ giới hạn giới hạn trên nào ở bất kỳ đâu trong chuỗi phụ thuộc. Nếu một cái gì đó vượt quá giới hạn phụ thuộc, thì rất có thể việc thêm hai gói như vậy sẽ phá vỡ giải pháp của bạn, vì vậy bạn chỉ nên chọn một gói - hoặc chỉ cần tránh chúng hoàn toàn, để bạn có thể thêm một gói trong tương lai. Đây là một quy tắc tốt, thực sự. Không bao giờ thêm thư viện vào phần phụ thuộc của bạn có giới hạn giới hạn trên quá mức. Khi tôi không tuân theo quy tắc này cho một gói lớn hơn, tôi thường hối hận

Nếu bạn đang thực hiện giới hạn và đang cung cấp thư viện, giờ đây bạn có cam kết phát hành bản cập nhật nhanh chóng, lý tưởng nhất là ngay trước khi bất kỳ phần phụ thuộc giới hạn nào ra mắt phiên bản mới. Mặc dù nếu bạn giới hạn, làm thế nào để bạn cài đặt các phiên bản phát triển hoặc thậm chí biết khi nào một phiên bản chính được phát hành?

Nó xung đột với giới hạn dưới chặt chẽ

Thư viện. ✅ (áp dụng)

Đăng kí. ❌ (không áp dụng)

Giới hạn dưới chặt chẽ chỉ xấu nếu các gói vượt quá giới hạn trên. Nếu bạn có thể tránh các gói giới hạn trên, bạn có thể chấp nhận các gói có giới hạn thấp hơn, điều này tốt hơn nhiều; . Một hệ thống đóng gói tốt sẽ cho phép bạn yêu cầu các gói hiện đại; . Hy vọng rằng bất kỳ ai đang viết phần mềm và thúc đẩy các phiên bản sẽ đồng ý rằng giới hạn dưới chặt chẽ tốt hơn nhiều so với giới hạn trên chặt chẽ, vì vậy nếu phải đi, đó là giới hạn trên

Cũng khá hiếm khi các gói giải quyết các giới hạn dưới trong CI (nhân tiện, tôi rất thích thấy một bộ giải như vậy trở thành một tùy chọn. ), do đó, đặt giới hạn dưới chặt chẽ là một cách để tránh các lỗi hiếm gặp khi các gói cũ được lưu trong bộ nhớ đệm mà bạn không thực sự hỗ trợ. CI hầu như không bao giờ có bộ đệm của các gói cũ, nhưng người dùng thì có

Nhân tiện, vui lòng kiểm tra với tệp


>>> range(0,10,2)

84 buộc giới hạn dưới của bạn ít nhất là nếu bạn có số lượng người dùng hợp lý

Giới hạn phụ thuộc che giấu sự không tương thích

Thư viện. ✅ (áp dụng)

Đăng kí. ✅ (áp dụng)

Một tác dụng phụ nghiêm trọng khác của việc giới hạn các phụ thuộc là bạn không được thông báo chính xác về sự không tương thích sắp đến và bạn phải chủ động hơn trong việc giám sát các phụ thuộc của mình để cập nhật. Nếu bạn không giới hạn các phụ thuộc của mình, bạn sẽ được thông báo ngay lập tức khi một phụ thuộc phát hành phiên bản mới, có thể là do CI của bạn, lần đầu tiên bạn xây dựng với phiên bản mới đó. Nếu bạn đang chạy CI với cờ


>>> range(0,10,2)

85 trên bản cài đặt pip của mình (không phổ biến, nhưng có lẽ là một ý tưởng hay), thì bạn thậm chí có thể phát hiện và khắc phục sự cố trước khi bản phát hành được thực hiện. Tuy nhiên, nếu bạn không làm điều này, thì bạn sẽ không biết về sự không tương thích cho đến (nhiều) sau này

Nếu bạn không tuân theo tất cả các phụ thuộc của mình, bạn có thể không nhận thấy rằng mình đã lỗi thời cho đến khi đó là một vấn đề nghiêm trọng đối với người dùng và bạn thực sự khó có thể biết thay đổi nào đã làm hỏng việc sử dụng của mình vì một số phiên bản đã được phát hành. Mặc dù tôi không phải là người hâm mộ cuồng nhiệt triết lý sống trực tiếp của Google (chủ yếu là vì nó có các yêu cầu nặng nề không áp dụng cho hầu hết các dự án nguồn mở), tôi đánh giá cao và thích nắm bắt sự không tương thích của phụ thuộc ngay khi bạn có thể;

Giới hạn tất cả các phụ thuộc che giấu sự không tương thích thực sự

Thư viện. ✅ (áp dụng)

Đăng kí. ✅ (áp dụng)

Nếu tôi thấy


>>> range(0,10,2)

86, điều đó cho tôi biết rằng bạn đang sử dụng các tính năng từ 1. 1 và không hỗ trợ 1. 0. Nếu tôi thấy

>>> range(0,10,2)

87, điều này sẽ cho tôi biết rằng có vấn đề với 1. 2 và phần mềm hiện tại (cụ thể là thứ mà bạn biết phần phụ thuộc sẽ không khắc phục/hoàn nguyên). Không phải là bạn chỉ giới hạn tất cả các phụ thuộc của mình và không biết liệu điều đó có hiệu quả hay không. Một chiếc mũ phải giống như một VIỆC CẦN LÀM; . Như trong ngày hôm qua

Thư viện yêu cầu bạn giới hạn

Thư viện. ✅ (áp dụng)

Đăng kí. ❌ (không áp dụng)

Có một số thư viện yêu cầu người dùng giới hạn. Nếu một thay đổi lớn sắp xảy ra và hầu hết mã người dùng sẽ bị hỏng, điều đó không tuyệt vời nhưng cần thiết. Nếu điều đó xảy ra, hãy giải thích nó trong readme và theo dõi nó với tư cách là người dùng nếu lời giải thích áp dụng cho bạn (hoặc tìm kiếm một thư viện khác không cố ý phá vỡ bạn). Thật không may, một số thư viện đang yêu cầu người dùng giới hạn, “đề phòng” cần thực hiện thay đổi vi phạm API. Điều này là sai vì nhiều lý do, đặc biệt là để viết thư viện

Đầu tiên, SemVer là cuộc chiến giữa những va chạm lớn thường xuyên và hiếm gặp; . Hướng này làm cho việc giới hạn các phiên bản chính trở nên vô nghĩa, bởi vì bạn có quá nhiều phiên bản. Hướng khác (là điều mà bất kỳ thư viện nào nghĩ rằng họ không có phiên bản chính nào sắp ra mắt) không phải là SemVer thực sự, đó là SemVer “thực tế”, và như tôi đã trình bày, bạn không nên quá chú trọng vào điều đó. Bạn không biết một phiên bản chính sẽ phá vỡ bạn và bạn không biết một phiên bản nhỏ / bản vá sẽ không gây ra các lỗi nhỏ (hoặc chính). Nếu điều đó quan trọng, bạn vẫn phải học cách sử dụng tệp khóa ứng dụng. Không có thẻ miễn phí ở đây. Đối với thư viện ngược dòng, về cơ bản, họ đang loại bỏ khả năng thực hiện các thay đổi vi phạm nhỏ hơn, chẳng hạn như xóa một mục không dùng nữa, bởi vì đó phải là một thay đổi lớn, nhưng một thay đổi lớn sẽ khiến một số lượng lớn người dùng không được hỗ trợ cho đến khi họ vượt qua . )

Phiên bản cũ phải được duy trì. Nếu người dùng được yêu cầu ghim vào phiên bản chính hoặc nếu bạn chỉ quyết định ghim vào phiên bản chính, thì phiên bản đó ít nhất phải có các bản cập nhật phụ thuộc phiên bản và bảo mật quan trọng (như hệ điều hành, kiến ​​trúc hoặc phiên bản Python mới) được áp dụng cho phiên bản đó cho đến khi tất cả . Nếu đó không phải là bản phát hành “LTS”, bạn không nên ghim vào nó, trừ khi bạn phải. Có một số LTS này trong Python, nhưng đó không phải là chuẩn mực xã hội - Python là OSS và hầu hết chúng ta không có tài nguyên hoặc ý định duy trì nhiều nhánh gói. Chúng tôi mong bạn sử dụng các bản phát hành mới nhất. Bạn có phụ thuộc vào gói hàng mà bạn biết là sẽ bị bỏ rơi không? . Nếu bạn giới hạn, thì đó là những gì bạn đang làm - thứ mà bạn phụ thuộc vào sẽ bị bỏ rơi khi bản phát hành tiếp theo được thực hiện. Điều này buộc bạn phải tạo một bản phát hành mới và có hiệu ứng nhỏ giọt;

Thật không may, những gì một số tác giả thư viện đang sử dụng điều này là một lượt miễn phí để phá vỡ mã của mọi người mà không có thời gian phản đối. Không phải ai cũng sẽ đọc README, v.v. và ngay cả khi họ đọc, họ có thể không thích giới hạn (ví dụ: vì lý do tiếp theo hoặc bất kỳ lý do nào khác được liệt kê ở đây) và không giới hạn thư viện của bạn. Vì vậy, một bản phát hành bị lỗi sẽ phá vỡ một số lượng người dùng tỷ lệ thuận với tổng số người dùng, bất kể bạn đưa gì vào README của mình. Đặc biệt là nếu bạn không đưa ra lý do và chỉ đưa ra lý do ở đó “đề phòng”;

Giải toán ngược thường sai

Thư viện. ✅ (áp dụng)

Đăng kí. ✅ (áp dụng)

Giả sử bạn phụ thuộc vào một thư viện, chúng tôi sẽ gọi nó là


>>> range(0,10,2)

88. Thư viện đó phụ thuộc vào

>>> range(0,10,2)

89. Bạn cũng phụ thuộc vào một thư viện

>>> list(range(5))

80. Điều đó cũng phụ thuộc vào

>>> list(range(5))

81. Khi cây 2. 1. 3 được thả ra, họ nhận thấy chúng bị gãy bởi bụi cây 7. 0, vì vậy họ giới hạn

>>> list(range(5))

82. Câu hỏi. bạn nghĩ người giải sẽ làm gì? . Trên thực tế, tôi đã dành khá nhiều thời gian để nói về các lỗi của bộ giải

Nhưng đây không phải là một trong số đó, không dành cho một bộ giải “thông minh” như chúng ta đang thấy ngày nay. Thay vào đó, bộ giải sẽ giải lại cây 2. 1. 2. Đây chắc chắn không phải là điều mà các nhà phát triển


>>> range(0,10,2)

88 mong muốn; . 1. 3 ở nơi đầu tiên. (Đây không chỉ là giả thuyết - IPython đã gặp phải vấn đề này với Jedi 0. 18)

Lưu ý rằng không giống như các lỗi mà bạn có thể thấy khi tải các phiên bản mới nhất, không tương thích (thường là lỗi thuộc tính, v.v.), các giới hạn này có thể tạo ra các phiên bản cũ với các lỗi tinh vi, lỗ hổng bảo mật hoặc chính xác các lỗi thuộc tính giống như vậy, và đây là . Họ không biết rằng họ đang nhận phiên bản cũ hoặc tại sao. Nếu họ đang sử dụng một thư viện giới hạn, họ sẽ không nhìn thấy nó và họ nghĩ rằng họ đang nhận được phiên bản mới nhất của mọi thứ

Nếu bạn thậm chí có một bản phát hành với giới hạn ít nghiêm ngặt hơn một bản phát hành mới hơn, thì điều này có thể xảy ra. Nếu Python có siêu dữ liệu có thể chỉnh sửa và mọi tác giả đều có thể được tin cậy để chỉnh sửa mọi bản phát hành trước đây với các chữ hoa phù hợp sau khi một bản được biết/phát hiện, thì hệ thống này sẽ ổn. Trên thực tế, conda-forge thực hiện chính xác điều này (mặc dù, quản trị viên conda-forge có thể thực hiện ghi đè siêu dữ liệu cho các bản phát hành trước đây, đây là một tính năng quan trọng mà PyPI sẽ không bao giờ thực hiện). Tuy nhiên, việc thay đổi điều này cho PyPI sẽ là một công việc lớn; . Sau đó, có ý nghĩa bảo mật - bạn sẽ làm gì nếu một số tạo ra một bản phát hành tốt sau đó thêm một phần phụ thuộc độc hại thông qua chỉnh sửa siêu dữ liệu?

Giải pháp khuyến nghị là giữ giới hạn ở mức tối thiểu tuyệt đối. Những sự cố này chỉ xảy ra khi bạn kết hợp giới hạn trên thấp với giới hạn dưới cao, do đó, ít giới hạn trên hơn sẽ giảm khả năng xảy ra sự cố này

Điều này giả định rằng bạn đang tham gia vào hệ sinh thái Phần mềm nguồn mở. Nếu tất cả những gì bạn kiểm soát tất cả các thư viện của mình (chẳng hạn như thường xảy ra trong ngành hoặc thậm chí trong một bộ sưu tập các thư viện được kết nối cao không được sử dụng độc lập với nhau, thì việc ghim giữa các phiên bản vẫn ổn - bạn kiểm soát tất cả các thư viện ghim lẫn nhau

Bây giờ, hãy làm cho điều này thậm chí còn thú vị hơn khi chuyển sang vấn đề tiếp theo của chúng ta. Giả sử, trước


>>> list(range(5))

85 0. 55 đã được phát hành, bạn yêu cầu

>>> list(range(5))

85 và bạn đang sử dụng Python 3. 10. Nếu bạn sử dụng Numba, có thể bạn đã thấy điều này - bạn không nhận được 0. 54, nhưng 0. 50, vì đó là phiên bản Numba chưa khai thác cuối cùng - và lỗi bạn thấy không phải là thiết lập đẹp. lỗi py cho bạn biết rằng Python quá mới, nhưng thay vào đó là lỗi biên dịch cho phần phụ thuộc Numba,

>>> list(range(5))

87. Không có trình cài đặt gói nào mà tôi biết xử lý chính xác giới hạn trên các phiên bản Python - và có thể không có cách "chính xác", nhưng đây cũng là vấn đề như trên, chỉ dành cho phiên bản Python. Nhưng nó trở nên tồi tệ hơn

Bây giờ, giả sử bạn đang sử dụng Python 3. 9 và bạn đang sử dụng trình quản lý gói khóa (Poetry, PDM, Pipenv,…). Bạn để giá trị mặc định cho Python caps, no cap hoặc đại loại như


>>> list(range(5))

88. Phiên bản Numba nào bạn sẽ nhận được? . 50. Được rồi, có lẽ bạn đã không đoán nó. Tại sao? . 50, vì nó hợp lệ trên Python 3. 99. Không bao giờ, không bao giờ đặt giới hạn vào khe siêu dữ liệu Python-Yêu cầu;

Ghim phiên bản Python là đặc biệt

Thư viện. ✅ (áp dụng)

Đăng kí. ✅ (áp dụng)

Thực hành bao phấn được thúc đẩy bởi Thơ đang thêm giới hạn trên cho phiên bản Python. Điều này đang lạm dụng một tính năng được thiết kế để giúp loại bỏ các phiên bản Python cũ để thay vào đó ngăn các phiên bản Python mới được sử dụng. “Cuộn ngược” qua các bản phát hành cũ hơn để tìm phiên bản mới nhất không hạn chế phiên bản Python đang được sử dụng chính xác là hành vi sai đối với giới hạn trên và đó là mục đích của trường này. Tất cả các bộ giải hiện tại (Pip, Thơ, PDM) không hoạt động chính xác nếu trường này bị giới hạn và thực hiện hành vi cuộn lại

Có một cuộc thảo luận về cách khắc phục điều này tại https. //bàn luận. con trăn. org/t/requires-python-upper-limits

Rõ ràng, điều này rất khác với một thư viện. cụ thể là bạn không thể hạ cấp phiên bản Python của mình nếu phiên bản này bị giới hạn ở mức thấp hơn phiên bản hiện tại của bạn. Bạn chỉ có thể thất bại. Vì vậy, điều này không "sửa chữa" một cái gì đó bằng cách lấy một phiên bản cũ hơn, đang hoạt động, nó chỉ gây ra lỗi nghiêm trọng nếu nó hoạt động theo cách bạn có thể hy vọng. Điều này có nghĩa là thay vì nhìn thấy lỗi thực sự và có thể giúp khắc phục nó, người dùng chỉ nhìn thấy lỗi "Python không khớp". Và, hầu hết thời gian, nó thậm chí không phải là một lỗi thực sự; . x không có cảnh báo, bạn nên hỗ trợ Python 3. x+1 (và 3. x+2 nữa)

Giới hạn tới


>>> list(range(5))

89 (đại loại như

>>> list(range(5))

88 trong Thơ) cũng mâu thuẫn trực tiếp với tuyên bố của chính nhà phát triển Python; . Nó không có khả năng xảy ra sớm và nếu có, nó có thể sẽ ảnh hưởng chủ yếu đến các bản dựng ABI ổn định / API có giới hạn và/hoặc việc sử dụng GIL; . Khi Python 4 ra mắt, sẽ rất khó để chạy CI của bạn trên 4 cho đến khi tất cả các phụ thuộc của bạn được giải phóng. Và bạn sẽ không thực sự nhìn thấy lỗi thực sự, bạn sẽ chỉ thấy các lỗi không tương thích, vì vậy bạn thậm chí sẽ không biết phải báo cáo gì với các thư viện đó. Và thực tiễn này khiến việc kiểm tra các phiên bản phát triển của Python trở nên khó khăn

Và, nếu bạn sử dụng Thơ, ngay khi ai đó giới hạn phiên bản Python, mọi dự án Thơ sử dụng nó cũng phải giới hạn, ngay cả khi bạn tin rằng đó là một hành vi đáng ghét và gây nhầm lẫn cho người dùng. Nó cũng sai trừ khi bạn ghim hoàn toàn phần phụ thuộc đã buộc giới hạn - nếu phần phụ thuộc loại bỏ nó trong bản phát hành bản vá hoặc thứ gì đó khác mà bạn hỗ trợ, bạn sẽ không cần giới hạn nữa. Tệ hơn nữa, nếu ai đó thêm giới hạn hoặc thắt chặt giới hạn, trừ khi họ giật từng bản phát hành cũ hơn, một trình giải khóa như Thơ hoặc PDM sẽ giải quyết ngược cho các phiên bản cuối cùng không có giới hạn để tệp khóa mà nó tạo ra sẽ “hợp lệ” trên . Điều này là do những bộ giải này đang sử dụng giới hạn cho tệp khóa - tệp khóa - tệp khóa không thể khóa phiên bản Python (một điểm khác biệt cơ bản khác), vì vậy chúng đang tính toán phạm vi phiên bản Python mà tệp khóa hợp lệ. Điều này khác với vị trí siêu dữ liệu Yêu cầu Python, nhưng cả Thơ và PDM đều không có cài đặt riêng. Nếu siêu dữ liệu có thể thay đổi (không phải vậy) và bạn thực sự tin tưởng các tác giả thư viện quay lại và kiểm tra mọi bản phát hành cũ để biết giới hạn Python chính xác (sẽ không xảy ra), giới hạn trên ở đây còn tệ hơn là vô dụng

Nếu bạn đang phát triển một gói như Numba, trong đó các chi tiết Python bên trong (mã byte) được dựa vào để thực sự có 0% khả năng gói đó hoạt động, hãy thêm lỗi theo cách thủ công vào thiết lập của bạn. py vẫn ổn, nhưng vẫn không giới hạn ở đây. Trường siêu dữ liệu này không được thiết kế để hỗ trợ chữ hoa trên và chữ hoa trên phải luôn chuyển thành lỗi; . Không bao giờ cung cấp giới hạn trên cho phiên bản Python của bạn. Tôi thường sẽ không sử dụng thư viện có giới hạn trên cho phiên bản Python; . Để rõ ràng, trong trường hợp đó, Python 3. 10 hoàn toàn ổn và bạn có thể cài đặt venv với 3. 9 và sau đó nâng cấp lên 3. 10 và nó vẫn hoạt động. Nó chỉ bị hỏng khi cài đặt với 3. 10 và cam kết trước. ci và brew đang cập nhật lên 3. 10, phá CI. Điều này khiến tôi mất hàng giờ để quay lại nửa tá repos, nó khiến những người tin tưởng vào các đề xuất phong cách của tôi cũng bị ảnh hưởng, tất cả chỉ vì giới hạn phiên bản chưa được kiểm tra - Python 3. 10 không phá vỡ ứng dụng nào cả

Thêm một phạm vi có giới hạn cho tệp khóa của bạn sẽ hoàn toàn ổn. Nhưng các hệ thống như Thơ và PDM hiện không phân biệt giữa khe siêu dữ liệu Yêu cầu-Python và phạm vi tệp khóa. Thêm giới hạn vào Yêu cầu-Python buộc người khác phải làm điều đó và phá vỡ khả năng khóa của người dùng

Các ứng dụng hơi khác nhau

Bây giờ nếu bạn có một ứng dụng thực sự (nghĩa là nếu bạn không có ý định sử dụng gói của mình làm thư viện), thì các ràng buộc của phiên bản cao hơn sẽ ít gặp vấn đề hơn. Bạn nhận thấy không phải tất cả các lý do trên áp dụng cho các ứng dụng. Điều này do hai lý do

Đầu tiên, nếu bạn đang viết một thư viện, thì “người dùng” của bạn đang chỉ định gói của bạn trong phần phụ thuộc của họ; . Đối với một ứng dụng, "người dùng" có nhiều khả năng sẽ cài đặt gói của bạn trực tiếp hơn, trong đó người dùng nói chung là các nhà phát triển khác bổ sung các yêu cầu cho thư viện

Thứ hai, đối với một ứng dụng được cài đặt từ PyPI, bạn sẽ ít phải lo lắng về những gì khác được cài đặt (các vấn đề khác vẫn đúng). Nhiều người dùng (hầu hết?) sẽ không sử dụng pipx hoặc môi trường ảo mới mỗi lần, vì vậy trên thực tế, bạn vẫn sẽ gặp sự cố với các ràng buộc chặt chẽ, nhưng có một cách giải quyết (ví dụ: sử dụng pipx). Tuy nhiên, bạn vẫn bị ảnh hưởng bởi hầu hết các đối số ở trên, vì vậy cá nhân tôi vẫn không khuyên bạn nên thêm các giới hạn chưa được kiểm tra

Bạn không bao giờ nên chỉ phụ thuộc vào SemVer cho một ứng dụng đã triển khai, chẳng hạn như một trang web. Tôi sẽ không lặp lại nguyên văn bài viết SemVer ở đây, nhưng nói chung, bạn gần như có khả năng bị hỏng (thường là không cố ý) từ bản phát hành nhỏ hoặc bản vá của thư viện hơn là từ phiên bản chính. Tùy thuộc vào độ ổn định và chất lượng của thư viện, thường có nhiều khả năng. Vì vậy, các ứng dụng chỉ có một sự lựa chọn. Họ nên cung cấp một tệp khóa có mọi phụ thuộc được liệt kê rõ ràng. Tất cả các hệ thống đều có khả năng thực hiện việc này - bạn có thể sử dụng các công cụ pip cho pip, Poetry, pdm và pipenv tự động tạo các tệp khóa, PEP 665 thậm chí còn đề xuất định dạng tệp khóa tiêu chuẩn, v.v. Điều này cung cấp cho người dùng cách cài đặt bằng cách sử dụng chính xác các phụ thuộc hoạt động đã biết. Trong quá trình sản xuất (giả sử đối với một trang web), bạn phải làm điều này. Nếu không, bạn sẽ ngẫu nhiên phá vỡ. Đây là lý do tại sao các bản phát hành bản vá tồn tại, đó là do bản phát hành bản vá chính, phụ hoặc thậm chí khác đã phá vỡ thứ gì đó

Nếu bạn không sử dụng Poetry hoặc pip-tools, bạn vẫn có thể tạo một tệp khóa đơn giản với


>>> range(1,10)

8

Sau đó, bạn có thể cài đặt nó với


>>> range(0,10,2)

8

Mặc dù điều này không bao gồm các hàm băm như Poetry, pipenv hoặc pip-tools, nhưng nó bao gồm nhiều trường hợp sử dụng có rủi ro thấp, chẳng hạn như thiết lập một ứng dụng web đơn giản. Nhân tiện, vì tôi đã quá khắt khe với Thơ, tôi nên chỉ ra rằng nó thực sự tỏa sáng ở đây vì mục đích sử dụng này

Điều gì về các yêu cầu chung của bạn để kiểm soát quá trình khóa? . Việc thêm các ghim tùy ý sẽ làm giảm khả năng cập nhật tệp khóa của bạn với các phụ thuộc mới nhất và che khuất những gì thực sự không được hỗ trợ với những gì được ghim tùy ý

Python không phải là JavaScript

Thơ lấy rất nhiều cảm hứng từ

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
11 cho JavaScript, bao gồm cả cú pháp toán tử

>>> range(1,10)

83 (có nghĩa là các bản phát hành bản vá/nhỏ mới hơn thì được nhưng không phải bản chính). Nếu bạn đến từ một ngôn ngữ như JavaScript, bạn có thể muốn sử dụng các ghim phía trên vì bạn đã quen nhìn thấy chúng ở đó. Nhưng có hai điểm khác biệt lớn giữa đóng gói Python và JavaScript

sự khác biệt kỹ thuật


>>> list(range(5))

8

Ví dụ về hệ thống phẳng (như Python) và hệ thống lồng nhau (như JavaScript). Các yêu cầu của

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
13 và
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
14 phải tương thích trong hệ thống phẳng

Sự khác biệt kỹ thuật là

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
11 (và JavaScript) có ý tưởng về các phụ thuộc cục bộ. Nếu bạn có một số gói yêu cầu cùng một phụ thuộc, thì mỗi gói sẽ nhận được một bản sao của phụ thuộc đó. Họ có thể tự do có các yêu cầu phiên bản xung đột; . Điều đó làm mất hiệu lực một số đối số ở trên. Việc thêm mã pin với tư cách là người dùng cũng khó hơn nhiều vì bạn phải thêm mã pin lồng nhau (bạn có thể sử dụng Yarn hoặc bạn có thể chỉnh sửa tệp khóa của mình theo cách thủ công). Nhân tiện, thơ không triển khai mô hình này - nó vẫn là một hệ thống Python truyền thống với các phụ thuộc được chia sẻ đầy đủ

Nhân tiện, điều này không giải quyết được tất cả các vấn đề. Nó chỉ giữ cho chúng khỏi xung đột ngẫu nhiên bằng cách giữ chúng được bản địa hóa - Các thư viện JavaScript hoạt động giống các ứng dụng hơn theo định nghĩa của tôi. Nó cũng có ý tưởng về các phụ thuộc ngang hàng (đối với plugin), có tất cả các vấn đề xung đột được liệt kê cho đến nay. Trên thực tế, đây là một trích dẫn từ nodejs. org về sự phụ thuộc ngang hàng

Một lời khuyên. các yêu cầu phụ thuộc ngang hàng, không giống như các yêu cầu dành cho phụ thuộc thông thường, nên dễ dãi

Trong Python, tất cả các phụ thuộc là "phụ thuộc ngang hàng"

khác biệt xã hội

Sự khác biệt về xã hội (bắt nguồn từ sự khác biệt về kỹ thuật) là các thư viện Python (và chính Python) không thích thực hiện các thay đổi khó, không tương thích ngược mà không có cảnh báo. Điều này được chấp nhận nhiều hơn trong các ngôn ngữ có gói cục bộ, nhưng gói Python ổn định phá vỡ khả năng tương thích ngược mà không có bất kỳ loại cảnh báo nào có thể tránh được nếu điều đó xảy ra quá thường xuyên. Nói chung có một khoảng thời gian không dùng nữa. Điều này đã được thực thi bởi quá trình chuyển đổi Python 3;

Yếu tố khác có thể chịu trách nhiệm cho sự khác biệt xã hội là các thư viện Python thường có một số lượng nhỏ người bảo trì, thường có các ưu tiên phân chia. Điều này có nghĩa là họ không thể dành tài nguyên để duy trì nhiều phiên bản phần mềm chính - thường chỉ hỗ trợ phiên bản mới nhất. Điều này có nghĩa là bạn phải sử dụng phiên bản chính và phụ mới nhất để được hỗ trợ các bản sửa lỗi bảo mật và tương thích; . (Với số lượng lỗ hổng được báo cáo ở trên, tôi không nghĩ rằng những người duy trì thư viện JavaScript sẽ phát hành nhiều bản phát hành bản vá mới cho các phiên bản chính cũ. )

Tôi quy tắc +3 cho giới hạn được đề xuất bởi NumPy. Nếu mã của bạn hoạt động mà không có cảnh báo trên phiên bản NumPy hiện tại

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
16, bạn nên giới hạn ở mức
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
17. Thế này tốt hơn; . Tốt hơn hết là bạn chỉ cần thừa nhận rằng nếu bạn sử dụng thư viện nhiều năm sau khi thư viện được phát hành, bạn có thể cần kiểm soát một số phiên bản theo cách thủ công. Nếu bạn biến mất khỏi Internet, thì một thư viện chưa được khai thác sẽ tương thích hơn một thư viện bị giới hạn sai và nếu bạn vẫn còn ở đó, bạn sẽ cập nhật nó. Tuy nhiên, nếu bạn sử dụng nhiều NumPy, đây có thể là giới hạn chấp nhận được. Vấn đề cũng sẽ ít hơn nhiều nếu bạn có một thư viện lớn với nhiều người bảo trì và các bản phát hành thường xuyên

Ngoài ra, trọng tâm của tôi ở đây là các thư viện nhỏ hơn; . Một thư viện lớn có các bản cập nhật thường xuyên có lẽ sẽ khá hợp lý với quy tắc +3 này

Theo dõi các cảnh báo

Vì vậy, Python và hệ sinh thái của nó không có giả định về SemVer nghiêm ngặt và có truyền thống đưa ra các cảnh báo không dùng nữa. Nếu bạn có CI tốt, bạn sẽ có thể nhận được các cảnh báo ngay cả trước khi người dùng của bạn nhìn thấy chúng. Hãy thử cấu hình pytest sau

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
1

Điều này sẽ biến các cảnh báo thành lỗi và cho phép CI của bạn bị hỏng trước khi người dùng bị hỏng. Bạn cũng có thể bỏ qua các cảnh báo cụ thể;

Phân tích một dự án JavaScript

Tôi đã và đang duy trì một số dự án gitbook nguồn mở và gitbook đã trở thành nguồn đóng từ nhiều năm trước. Tôi quyết định thực hiện phân tích về tệp khóa cho Modern CMake

Đối với dự án, có 2 hoặc 7 gói cấp độ người dùng (gitbook và svgexport, cũng như năm plugin gitbook). Điều này cài đặt 576 gói, sẽ được làm phẳng thành 315 gói duy nhất nếu bạn bỏ qua ghim phiên bản hoặc 426 gói nếu bạn bao gồm phiên bản cùng với gói - vâng, đó là hơn 100 lần một gói được cài đặt nhiều lần với các phiên bản khác nhau. Mặc dù không phải tất cả đều là xung đột (npm dường như không cố gắng lắm để có được các phiên bản nhất quán), nhưng tôi vẫn đếm được ít nhất 30 xung đột phiên bản không thể giải quyết được nếu đây là một hệ thống phẳng. Đây chính xác là những gì chúng ta sẽ gặp phải nếu chúng ta cố gắng sao chép giới hạn phiên bản trong một hệ thống phụ thuộc phẳng như Python và nhiều người hơn bắt đầu tuân theo giới hạn phiên bản - nó không mở rộng quy mô trong một hệ thống phẳng

Ngoài ra, tòa nhà này hôm nay báo cáo 153 lỗ hổng (11 thấp, 47 trung bình, 90 cao, 5 nghiêm trọng). Và nhiều gói bị kẹt trên các phiên bản cũ hơn tới 10 phiên bản chính - và điều này thậm chí chỉ tính svgexport, vì gitbook là một dự án đã chết (về nguồn mở)

Mã phân tích nhanh và bẩn (bấm để mở rộng)

Đây là mã phân tích (xấu xí) mà tôi đã sử dụng để xử lý tệp

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
18. Các xung đột cuối cùng được đếm bằng tay, vì hệ thống không đủ gần với Python để sử dụng
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
19. Đây không phải là mã bóng bẩy, đẹp đẽ, mà là lần lặp lại đầu tiên của tôi, với các tên viết tắt ban đầu và như vậy mà tôi thường không để bất kỳ ai nhìn thấy. Việc đếm cuối cùng cho các xung đột được thực hiện thủ công dựa trên các ràng buộc đơn giản hóa được in ra

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
2

Giới hạn trên đôi khi hợp lệ

Khi nào thì được phép đặt giới hạn trên?

Lý do hợp lệ để thêm giới hạn trên là

  1. Nếu một phụ thuộc được biết là bị hỏng, hãy chặn phiên bản bị hỏng. Hãy cố gắng hết sức để khắc phục sự cố này một cách nhanh chóng, sau đó xóa khối nếu bạn có thể khắc phục được sự cố này. Nếu quá trình sửa lỗi diễn ra ngược dòng, chỉ loại trừ phiên bản bị hỏng là được (hoặc họ có thể “lấy” bản phát hành bị lỗi để giúp mọi người)
  2. Nếu bạn biết ngược dòng sắp thực hiện một thay đổi lớn rất có khả năng phá vỡ mức sử dụng của bạn, bạn có thể giới hạn. Nhưng hãy cố gắng khắc phục sự cố này nhanh nhất có thể để bạn có thể tháo nắp khi chúng phát hành. Có thể thêm nhánh phát triển/thử nghiệm phát hành cho đến khi vấn đề này được giải quyết. Ví dụ, TensorFlow 1-2 là một thay đổi thực sự lớn đã làm thay đổi mọi thứ xung quanh. Nhưng sửa nó thực sự đơn giản như nhập từ
    click>=7,<8
    # Equivalent, the tilde lets the final number be larger
    click~=7.0
    
    20
  3. Nếu ngược dòng yêu cầu người dùng giới hạn thì tôi vẫn không thích, nhưng bạn muốn làm theo khuyến nghị ngược dòng thì không sao. Bạn nên tự hỏi mình. bạn có muốn sử dụng một thư viện có thể cố tình làm hỏng bạn và yêu cầu bạn thay đổi mà không cần trợ giúp trong thời gian ngừng sử dụng không? . Ngoài ra, nếu bạn đang ngược dòng, việc phá vỡ người dùng mà không có cảnh báo phản đối trước tiên là rất phi Pythonic. Đừng làm điều đó nếu có thể. Một thượng nguồn tốt (như NumPy) có thể yêu cầu giới hạn trong tương lai (NumPy yêu cầu +3 phiên bản cho các gói phụ thuộc lớn)
  4. Nếu bạn đang viết tiện ích mở rộng cho hệ sinh thái/khung (tiện ích mở rộng pytest, tiện ích mở rộng Sphinx, tiện ích mở rộng Jupyter, v.v.), thì việc giới hạn phiên bản chính của thư viện đó là chấp nhận được. Lưu ý điều này xảy ra một lần - bạn có một thư viện duy nhất có thể được giới hạn. Bạn phải phát hành ngay khi có thể sau một bản phát hành chính mới và bạn nên theo dõi chặt chẽ từ đầu nguồn - có thể sử dụng các bản phát hành phát triển để thử nghiệm, v.v. Nhưng làm điều này cho một thư viện có thể quản lý được
  5. Bạn đang phát hành hai hoặc nhiều thư viện đồng bộ với nhau. Bạn kiểm soát nhịp phát hành cho cả hai thư viện. Đây có thể là lý do “tốt nhất” để giới hạn. Một số vấn đề trên không áp dụng trong trường hợp này - vì bạn kiểm soát nhịp phát hành và có thể giữ chúng đồng bộ
  6. Bạn phụ thuộc vào các chi tiết nội bộ riêng tư của thư viện. Bạn cũng nên suy nghĩ lại về các lựa chọn của mình - điều này có thể bị hỏng trong một bản phát hành nhỏ hoặc bản vá và thường là (pyparsing 3. 0. 5 chẳng hạn)

Nếu bạn giới hạn trong những tình huống này, tôi sẽ không phàn nàn, nhưng tôi cũng không thực sự khuyên bạn nên làm điều đó

  1. Nếu bạn phụ thuộc nhiều vào thư viện, có thể giới hạn. Một bề mặt API thực sự lớn có nhiều khả năng bị ảnh hưởng bởi sự cố có thể xảy ra
  2. Nếu một thư viện rất mới, chẳng hạn như trên phiên bản 1 hoặc thư viện ZeroVer và có rất ít người dùng, có thể giới hạn nếu nó có vẻ không ổn định. Xem liệu các tác giả thư viện có đề xuất giới hạn (lý do 3 ở trên) hay không - họ có thể lên kế hoạch thực hiện một thay đổi lớn nếu nó đang ở giai đoạn đầu của quá trình phát triển. Đây không phải là quyền chung để giới hạn các thư viện ZeroVer
  3. Nếu một thư viện có vẻ thực sự không ổn định, chẳng hạn như có lịch sử thực hiện các thay đổi lớn, thì hãy giới hạn. Hoặc sử dụng một thư viện khác. Tốt hơn nữa, hãy liên hệ với các tác giả và đảm bảo rằng việc sử dụng của bạn an toàn trong tương lai gần

Tất cả đều là những trường hợp đặc biệt và không phổ biến; . Trong mọi trường hợp khác, không giới hạn các phụ thuộc của bạn, đặc biệt nếu bạn đang viết một thư viện. Bạn có lẽ có thể tóm tắt nó như thế này. nếu có khả năng cao (giả sử 75%+) rằng một phụ thuộc sẽ bị hỏng khi cập nhật, bạn có thể thêm giới hạn. Nhưng nếu không có lý do gì để tin rằng nó sẽ bị hỏng, thì đừng thêm nắp;

Nếu bạn có một ứng dụng thay vì thư viện, bạn có thể thận trọng hơn một chút, nhưng không nhiều. Ứng dụng không nhất thiết phải sống trong môi trường dùng chung, mặc dù chúng có thể

Lưu ý rằng nhiều trường hợp ở trên là do tương tác rất gần/đặc biệt với một số lượng nhỏ thư viện (có thể là plugin cho khung, bản phát hành được đồng bộ hóa hoặc mức sử dụng rất nặng). Hầu hết các thư viện bạn sử dụng không thuộc danh mục này. Hãy nhớ rằng, các tác giả thư viện không muốn phá vỡ những người dùng theo dõi tài liệu và API công khai của họ. Nếu họ làm như vậy, đó là vì một lý do đặc biệt và chính đáng (hoặc đó là một thư viện tồi để phụ thuộc vào). Họ có thể sẽ có một khoảng thời gian không dùng nữa, đưa ra cảnh báo, v.v.

Nếu bạn thực hiện giới hạn phiên bản cho bất kỳ thứ gì, bạn hứa sẽ tuân thủ chặt chẽ sự phụ thuộc đó, cập nhật giới hạn càng sớm càng tốt, theo dõi các bản phát hành beta hoặc RC hoặc nhánh phát triển, v.v. Khi có phiên bản mới của thư viện, người dùng cuối có thể bắt đầu dùng thử. Nếu họ không thể, các phần phụ thuộc của thư viện của bạn là một bản tóm tắt bị rò rỉ (người dùng không cần phải quan tâm đến thư viện phần phụ thuộc nào sử dụng)

Điều gì về yêu cầu thời gian xây dựng?

Điều này luôn được sử dụng trong một môi trường ảo sạch sẽ, vì vậy rất hấp dẫn để giới hạn hoặc thậm chí ghim. Tuy nhiên, đồng thời, việc giới hạn những thứ này cũng có thể bị hỏng nếu bạn cố gắng sử dụng các hệ thống mới, Python mới, v.v. Vì vậy, như mọi khi, hãy tự hỏi mình. Làm thế nào có khả năng là điều này để phá vỡ? . Một số trường hợp đặc biệt -

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
24 không bao giờ được xuất hiện trực tiếp (nếu ít nhất là biên dịch dựa trên C-API), luôn sử dụng
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
25 thay thế (không giới hạn/không ghim)

Tôi sẽ xem xét

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
26;

Cập nhật nhanh có thể che giấu vấn đề

Nếu một tác giả thư viện cập nhật rất nhanh thư viện của họ khi các bản phát hành mới ra mắt (chẳng hạn như phong phú), thì giới hạn trên không gây ra sự cố ngay lập tức (mặc dù nó vẫn ảnh hưởng đến các phiên bản phát triển thử nghiệm). Tuy nhiên, ngay sau khi những cập nhật nhanh chóng đó dừng lại, thư viện bắt đầu phân rã nhanh hơn nhiều so với thư viện không có chữ hoa trên. Các phần phụ thuộc không thể "sửa" lỗi này bằng cách phát hành một phiên bản mới có backport của bất kỳ thứ gì họ đã xóa/thay đổi vì họ cũng không nhận ra ai đó đang sử dụng nó, bởi vì họ đã bị giới hạn. Điều này dẫn đến lý do tiếp theo để sử dụng mũ

lỗi thời có kế hoạch

Có thêm một lý do để thêm giới hạn phiên bản cao hơn mà tôi không bao gồm ở trên. Nếu cuối cùng bạn dự định thay đổi giấy phép của thư viện và/hoặc biến nó thành nguồn đóng, thì việc thêm giới hạn phiên bản cao hơn sẽ đảm bảo rằng các phiên bản nguồn mở, cũ của thư viện sẽ nhanh chóng trở nên lỗi thời. Sử dụng giới hạn phiên bản cao hơn buộc người dùng của bạn phải phụ thuộc vào bạn để cập nhật phiên bản thường xuyên. Nếu bạn dừng và chuyển sang một mô hình khác, mã của bạn sẽ nhanh chóng có thể gỡ cài đặt được với các bản cập nhật bảo mật mới nhất, trên các phiên bản Python mới hơn, trên phần cứng hoặc hệ điều hành mới hơn hoặc với các thư viện mới hơn. Điều này thường là bắc cầu;

Điều này cuối cùng có thể xảy ra nếu bạn không giới hạn, nhưng nó sẽ xảy ra nhanh hơn nhiều và đảm bảo hơn với các giới hạn cứng. Hãy nhớ rằng người dùng không thể sửa giới hạn

Ví dụ về giới hạn chấp nhận được

Numba ghim chính xác LLVMLight và đặt giới hạn cứng cho Python (và gần đây, tạm thời cũng là NumPy). Họ kiểm soát cả Numba và LLVMLight, vì vậy việc ghim ở đó không sao (lý do 5 ở trên). Họ sử dụng Python bytecode để dịch ngược các hàm Python; . Họ biết phiên bản NumPy mới nhất không tương thích (lý do 1 ở trên). Trong cả hai trường hợp, họ cũng đánh dấu vào

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
27, nhưng hãy nhớ rằng, điều đó chỉ ảnh hưởng đến việc xây dựng Numba, do đó, nó hoạt động với Python, nhưng có thể không hoạt động đối với các phụ thuộc thông thường như NumPy vì người dùng thông thường cài đặt bánh xe chứ không phải SDists, vì vậy hãy thiết lập. py không chạy. Numba nên (và sẽ) giải phóng mã pin này càng nhanh càng tốt, vì có khá nhiều lý do để sử dụng NumPy 1. 21, bao gồm cả việc nó là NumPy đầu tiên hỗ trợ Python 3. 10

Cá nhân tôi giới hạn lịch sử đối với bản phát hành nhỏ của biểu đồ tăng cường. Tôi kiểm soát cả hai gói và phát hành chúng đồng bộ; . Chúng được kết hợp chặt chẽ, nhưng là một phần của một gia đình (lý do 1 ở trên). Tại thời điểm này, biểu đồ tăng cường có khả năng đủ ổn định ngay cả trong các chi tiết bên trong để giảm bớt một chút, nhưng bằng cách này, tôi cũng có thể quảng cáo các tính năng biểu đồ tăng cường mới dưới dạng các tính năng lịch sử mới. ;)

Nhiều gói tuân theo đề xuất của Flit và sử dụng

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
28 trong pyproject. thông số kỹ thuật xây dựng toml. Đây là lý do 3 ở trên; . Nó cũng nằm trong pyproject. toml, theo định nghĩa sẽ không bao giờ được “chia sẻ” với bất kỳ thứ gì, đó là một môi trường ảo mới, dùng một lần được tạo ra khi xây dựng một bánh xe và sau đó bị vứt bỏ, khiến nó giống như một yêu cầu của ứng dụng hơn. Tuy nhiên, nếu bạn chỉ sử dụng cấu hình PEP 621 cho Flit, tôi thấy không có lý do gì để giới hạn nó; . Và Flit thực sự phản ánh điều này trong đề xuất giới hạn phiên bản

Ví dụ về mũ xấu

TenorFlow

Bây giờ hãy xem xét một giới hạn trên không tốt và sự lộn xộn mà nó gây ra. TensorFlow dùng để đặt một (chú ý một số comment có chỗ sai, ví dụ thứ tự không quan trọng khi giải). Đây là một mớ hỗn độn. Một số phụ thuộc ở đây là các thư viện nhỏ sẽ không phá vỡ bất kỳ ai khi cập nhật, như

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
29 và
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
30. Có lẽ điều tồi tệ nhất trong tất cả là
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
31. Đây là mô-đun backport cho mô-đun
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
32 của thư viện tiêu chuẩn và được ghim vào
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
33. Đầu tiên, các phiên bản mới của
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
31 sẽ không xóa bất kỳ thứ gì trong ít nhất 5 năm và có thể không bao giờ - đây là một cổng sau tương thích (stdlib
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
32 có thể bị xóa sau 5 năm). Thứ hai, vì đây là một cổng sau, nên việc đặt giới hạn cao dưới cho điều này là rất, rất phổ biến - nếu bạn muốn sử dụng Python 3. 10 tính năng, bạn phải đặt cận dưới cao hơn. Đen, , đặt
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
36 ở mức tối thiểu. Điều này hoàn toàn hợp lệ, IMO - nếu bạn có gói backport và bạn muốn backport từ Python 3. 10, bạn sẽ có thể có được chúng. Được rồi, vì vậy hãy nói rằng bạn chạy cái này

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
3

(hoặc giả vờ rằng đó là trong tệp

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
37 cho một dự án, v.v. - tuy nhiên bạn muốn nghĩ về điều đó). Đầu tiên, trình giải quyết sẽ tải xuống màu đen 21. 8b0. Sau đó, nó sẽ bắt đầu tải xuống các bánh xe TensorFlow, hoạt động trở lại một số phiên bản - nếu bạn đang sử dụng kết nối băng thông hạn chế, hãy lưu ý rằng mỗi phiên bản có dung lượng vài trăm MB, đây là nhiều GB để thực hiện. Cuối cùng, nó sẽ bỏ cuộc và bắt đầu thử các phiên bản màu đen cũ hơn. Cuối cùng, nó sẽ tìm thấy một bộ tương thích, vì các phiên bản màu đen cũ hơn không có chân cắm cao và sẽ cài đặt bộ đó. Bây giờ hãy thử điều này

click>=7
2

Điều này sẽ buộc

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
38 được khôi phục và sau đó sẽ bị hỏng với

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
0

Nói cách khác, chỉ cần cài đặt sẵn màu đen sẽ khiến bạn không thể cài đặt TensorFlow, mặc dù chúng hoàn toàn không liên quan. Nguyên nhân? . 7 thay vì 3. 10, cái nào sai. Đây thực sự là một gói backport thư viện tiêu chuẩn (dành cho

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
32). Với tính năng ghim mạnh mẽ như vậy, TensorFlow thực sự là một ứng dụng, nó không thể chơi tốt với bất kỳ thư viện nào khác

Do sự cố này gây ra, Tensorflow có giới hạn trên đối với hầu hết các phụ thuộc và giờ đây bạn có thể cài đặt lại nó với các thư viện khác

Bao bì và PyParsing

Bao bì là một thư viện nền tảng cho hầu hết các bao bì Python. Mọi người đều phụ thuộc vào nó (tox, cibuildwheel, pdm, v.v.) hoặc nhà cung cấp nó (pip, Thơ, pipenv). Bao bì có rất ít phụ thuộc, nhưng nó yêu cầu pyparsing. Trong phiên bản 3, pyparsing đã thay đổi tên của một số mã thông báo - nhưng cung cấp các tên tương thích ngược. Bao bì chỉ hoạt động tốt với phiên bản 3, nhưng nó ảnh hưởng đến văn bản của một thông báo lỗi được so sánh trong các thử nghiệm, vì vậy bao bì giới hạn pyparsing thành

click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
6, sau đó phát hành bao bì 21. 2 không có thay đổi nào khác (so với
click>=7
22) ngoại trừ nắp này. Điều này ngay lập tức bắt đầu phá vỡ mọi thứ (như triển khai Google App Engine và các khiếu nại khác nêu rõ “rất nhiều xung đột phụ thuộc”). Rõ ràng, nó không giải quyết được bất cứ điều gì ngoại trừ một bài kiểm tra bên trong chính bao bì. Sau đó pyparsing 3. 0. 5 đã được phát hành với sự thay đổi tên phương thức nội bộ (bắt đầu bằng dấu gạch dưới). Điều này đã được sử dụng bởi bao bì (xấu), vì vậy giới hạn thực sự là
click>=7,<8
# Equivalent, the tilde lets the final number be larger
click~=7.0
7 (pyparsing rất tốt và đã khôi phục điều này cho
click>=7
24, mặc dù họ có thể nói rằng đó là lỗi của bao bì - đúng vậy). Cách khắc phục chính xác là không sử dụng chi tiết triển khai riêng, bao bì đã được sửa, nhưng các phiên bản cũ vẫn tồn tại

TL;DR

Giới hạn phụ thuộc có tác động tiêu cực lâu dài, đặc biệt là đối với các thư viện và không bao giờ được xem nhẹ. Thư viện không được cài đặt riêng lẻ; . Chỉ thêm giới hạn nếu phần phụ thuộc được biết là không tương thích hoặc có khả năng cao (>75%) phần phụ thuộc không tương thích trong bản phát hành tiếp theo. Không giới hạn theo mặc định - giới hạn phụ thuộc làm cho phần mềm của bạn không tương thích với các thư viện khác cũng có giới hạn thấp hơn nghiêm ngặt đối với phụ thuộc và giới hạn các bản sửa lỗi trong tương lai. Bất kỳ ai cũng có thể sửa giới hạn bị thiếu, nhưng người dùng không thể sửa giới hạn quá hạn chế gây ra lỗi bộ giải. Nó cũng khuyến khích ẩn các vấn đề cho đến khi chúng trở nên khó sửa chữa hơn, nó không mở rộng sang các hệ thống lớn hơn, nó hạn chế khả năng truy cập các bản cập nhật bảo mật và sửa lỗi của bạn và một số công cụ (Poetry) buộc những người dùng hạ nguồn của bạn phải đưa ra những quyết định tồi tệ này nếu bạn đưa ra chúng. Không bao giờ giới hạn Python, về cơ bản nó đã bị hỏng vào lúc này. Ngoài ra, ngay cả việc đóng nắp bao bì cũng có những hậu quả tiêu cực có thể tạo ra những giải pháp không mong muốn

Ngay cả SemVer hoàn hảo cũng không hứa hẹn việc sử dụng của bạn sẽ bị hỏng và dù sao thì không có thư viện nào thực sự có thể tuân theo SemVer một cách hoàn hảo; . Bạn phải học cách sử dụng hệ thống gói khóa nếu bạn cần độ tin cậy của ứng dụng - Giới hạn SemVer không thể thay thế. Python có văn hóa sử dụng các cảnh báo không dùng nữa và chuyển đổi chậm, không giống như hệ sinh thái có hệ thống phụ thuộc lồng nhau như npm. Chúng tôi đã thấy một dự án NPM thực tế có hơn 30 xung đột phiên bản nếu nó được làm phẳng như Python - giới hạn phiên bản không mở rộng khi chia sẻ các phụ thuộc. Cung cấp một tập hợp các ràng buộc được ghim đầy đủ tùy chọn nếu điều đó quan trọng đối với bạn đối với các ứng dụng - đây là cách duy nhất để đảm bảo một tập hợp phụ thuộc hoạt động lâu dài (bao gồm cả cho npm)

Nếu nhất thiết phải đặt giới hạn trên, bạn nên phát hành phiên bản mới càng sớm càng tốt với giới hạn cao hơn khi phần phụ thuộc cập nhật (lý tưởng là trước khi phần phụ thuộc phát hành bản cập nhật). Nếu bạn đang cam kết điều này, tại sao không nhanh chóng phát hành một bản phát hành bản vá có giới hạn chỉ sau khi xung đột thực sự xảy ra? . Bạn muốn người dùng sử dụng các phiên bản mới nhất của thư viện nếu có sự cố, vậy tại sao bạn không thể đưa ra sự cân nhắc tương tự cho các thư viện mà bạn phụ thuộc và sử dụng?

Nếu bạn cần TL;DR cho TL;DR, tôi sẽ chỉ trích dẫn Thành viên Hội đồng Chỉ đạo Python và chuyên gia đóng gói Brett Cannon

Các thư viện/gói phải được đặt giới hạn và nếu cần, loại trừ các phiên bản lỗi đã biết, nhưng nếu không thì không giới hạn phiên bản tối đa vì bạn không thể dự đoán khả năng tương thích trong tương lai

Ngoài ra, điều này không thể khái quát hóa đối với các hệ thống có thể cung cấp các phiên bản duy nhất cho từng gói - như Node. js. Các hệ thống này có thể tránh xung đột trình giải quyết bằng cách cung cấp các phiên bản khác nhau cục bộ cho từng gói; . Điều này rất, rất khác với một hệ thống luôn giải quyết cho một phiên bản dùng chung. Những hệ thống đó cũng là nơi cú pháp


>>> range(1,10)

83 hữu ích hơn nhiều. Một số công cụ (như Thơ) dường như đang cố gắng áp dụng một phần của các hệ thống đó (ví dụ: cú pháp dấu mũ) mà không áp dụng tính năng phiên bản cục bộ, đây là chìa khóa cho cách chúng hoạt động. Việc có các bản sao cục bộ (trên mỗi phần phụ thuộc) của tất cả các phần phụ thuộc giải quyết được nhiều vấn đề ở trên và thực tế biến các thư viện thành ứng dụng, mặc dù một số đối số ở trên vẫn được áp dụng, chẳng hạn như ẩn tính không tương thích cho đến khi bộ thay đổi rất lớn

Sự nhìn nhận

Cảm ơn thành viên hội đồng chỉ đạo Python Brett Cannon, nhà phát triển cốt lõi Python Paul Ganssle, các thành viên PyPA Bernát Gábor, Pradyun Gedam và @layday, thành viên RSE Troy Comi và thành viên IRIS-HEP Alex Held vì những nhận xét của họ về các bản nháp ban đầu. Ngoài ra, tôi muốn ghi nhận bài viết xuất sắc Tại sao bạn không nên gọi thiết lập. py trực tiếp từ Paul Ganssle vì đã thuyết phục tôi rằng một bài viết quái dị kiểu Proustian có thể hữu ích. Tất cả lỗi chính tả và lỗi là của riêng tôi


  1. Thơ đang ưu tiên tính trung thực của file khóa đây. Nếu bạn tạo một tệp khóa (và Thơ luôn làm như vậy) và một chân phụ thuộc python <3. 10, thì tệp khóa đó sẽ không tải trên Python 3. 10. Điều này có thể hiểu được, nhưng không có cách nào để đặt vị trí siêu dữ liệu Yêu cầu-Python ngoài cài đặt này. Nếu bạn đang phát triển một thư viện, bạn không nên buộc phải làm điều này vì tệp khóa thậm chí không có trong bản phân phối. Tôi chỉ muốn cảnh báo + phạm vi Python chính xác trong tệp khóa hoặc cách đặt chúng riêng biệt, như với hỗ trợ siêu dữ liệu PEP 621 kết hợp với thông số kỹ thuật cũ.  

  2. Sự thật thú vị. một phím tắt bao gồm kiểm tra xem phiên bản mới nhất của mọi thứ có hợp lệ không. Điều này ngay lập tức bị hỏng nếu có giới hạn trên ảnh hưởng đến việc giải quyết.  

  3. Một nhược điểm phổ biến ở đây là bộ giải phụ thuộc thông minh sẽ nhận được các phiên bản cũ, vì vậy việc cập nhật không được nhấn. Nhưng các thư viện mới không cần phải hỗ trợ các phiên bản thực sự cũ của mọi thứ chỉ vì chúng không thể sống chung với các thư viện có chữ hoa cũ. Các thư viện không cần phải tiếp tục đẩy các bản cập nhật cho các bản phát hành chính/phụ cũ để hỗ trợ các phiên bản Python và phần cứng mới, v.v. Vì vậy, vâng, bạn “hứa hẹn” sẽ cập nhật nhanh chóng nếu cập nhật phụ thuộc giới hạn. Nếu không, thư viện của bạn không thể phụ thuộc vào.  

  4. Rõ ràng là tôi đang đưa ra các giả định về bạn, độc giả của tôi, ở đây. Nhưng tôi khá mong đợi tôi đúng. Nếu không, tôi muốn bạn tham gia tất cả các dự án của tôi và bắt đầu phát hành các backport cũ cho tất cả các phiên bản chính cho tôi. ;)

  5. Trong pip hoặc Thơ. Conda có thể làm điều này, bởi vì Python giống như một thư viện ở đó. Nhưng chúng tôi không thảo luận về conda, và ít nhất conda-forge có hệ thống riêng và nó hoàn toàn không bị ràng buộc với cấu hình bao bì thông thường của bạn, tên gói thậm chí có thể không giống nhau, v.v.  

  6. Điều này được thực hiện để cung cấp đối số này, không chỉ để chơi với Python 3. 10 khớp mẫu vì tôi luôn làm việc trên thư viện và không được chơi với tất cả đồ chơi mới…

  7. Mặc dù, với tư cách là người bảo trì gói conda-forge Numba, tôi phải nói rằng nó làm cho bản cập nhật chậm hơn do không có chỗ ngọ nguậy và vì lý do nào đó, Numba dường như phát hành trước khi llvmlite phù hợp có sẵn trên PyPI.  

    Giới hạn trên và dưới trong Python là gì?

    Cận dưới ban đầu là chỉ số 0 và cận trên ban đầu là chỉ số cuối cùng của chuỗi .

    Giới hạn trên với ví dụ là gì?

    danh từ Toán học. một phần tử lớn hơn hoặc bằng tất cả các phần tử trong tập hợp đã cho . 3 và 4 là cận trên của tập hợp gồm 1, 2 và 3.

    Công thức giới hạn trên là gì?

    Để tìm cận trên và cận dưới của một số đã được làm tròn. Xác định giá trị vị trí của mức độ chính xác đã nêu. Chia giá trị địa điểm này cho 2. Cộng số này với giá trị đã cho để tìm cận trên, trừ số này cho giá trị đã cho để tìm cận dưới

    Giới hạn trên của hàm là gì?

    Về mặt hình học, giới hạn trên là. đường nằm ngang mà đồ thị của hàm số không vượt lên trên . Tương tự, cận dưới là đường nằm ngang mà đồ thị không đi. phía dưới.