Hướng dẫn python attrs frozen - trăn đông lạnh

Basics#

Cách sử dụng đơn giản nhất có thể là:

>>> from attrs import define, field
>>> @define
... class Empty:
...     pass
>>> Empty()
Empty()
>>> Empty() == Empty()
True
>>> Empty() is Empty()
False

Vì vậy, nói cách khác:

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 là hữu ích ngay cả khi không có thuộc tính thực tế!

Nhưng bạn thường muốn một số dữ liệu trên các lớp học của mình, vì vậy hãy để thêm một số:

>>> @define
... class Coordinates:
...     x: int
...     y: int

Theo mặc định, tất cả các tính năng được thêm vào, vì vậy bạn ngay lập tức có một lớp dữ liệu đầy đủ chức năng với một chuỗi

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
6 đẹp và các phương thức so sánh.

>>> c1 = Coordinates(1, 2)
>>> c1
Coordinates(x=1, y=2)
>>> c2 = Coordinates(x=2, y=1)
>>> c2
Coordinates(x=2, y=1)
>>> c1 == c2
False

Như được hiển thị, phương thức

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
7 được tạo cho phép cả đối số vị trí và từ khóa.

Đối với các thuộc tính riêng tư,

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 sẽ loại bỏ các dấu gạch dưới hàng đầu cho các đối số từ khóa:

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)

Nếu bạn muốn tự khởi tạo các thuộc tính riêng tư của mình, bạn cũng có thể làm điều đó:

>>> @define
... class C:
...     _x: int = field(init=False, default=42)
>>> C()
C(_x=42)
>>> C(23)
Traceback (most recent call last):
   ...
TypeError: __init__() takes exactly 1 argument (2 given)

Một cách bổ sung để xác định các thuộc tính cũng được hỗ trợ. Điều này rất hữu ích trong những lúc bạn muốn tăng cường các lớp không phải là của bạn (tốt đẹp

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
9 cho các mô hình django bất cứ ai?):

>>> class SomethingFromSomeoneElse:
...     def __init__(self, x):
...         self.x = x
>>> SomethingFromSomeoneElse = define(
...     these={
...         "x": field()
...     }, init=False)(SomethingFromSomeoneElse)
>>> SomethingFromSomeoneElse(1)
SomethingFromSomeoneElse(x=1)

Phân lớp là xấu cho bạn, nhưng

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 vẫn sẽ làm những gì bạn hy vọng:

>>> @define(slots=False)
... class A:
...     a: int
...     def get_a(self):
...         return self.a
>>> @define(slots=False)
... class B:
...     b: int
>>> @define(slots=False)
... class C(B, A):
...     c: int
>>> i = C(1, 2, 3)
>>> i
C(a=1, b=2, c=3)
>>> i == C(1, 2, 3)
True
>>> i.get_a()
1

Các lớp có rãnh, là mặc định cho các API mới, don lồng chơi tốt với nhiều kế thừa vì vậy chúng tôi không sử dụng chúng trong ví dụ., which are the default for the new APIs, don’t play well with multiple inheritance so we don’t use them in the example.

Thứ tự của các thuộc tính được xác định bởi MRO.

Thuộc tính chỉ từ khóa#

Bạn cũng có thể thêm các thuộc tính chỉ từ khóa:

>>> @define
... class A:
...     a: int = field(kw_only=True)
>>> A()
Traceback (most recent call last):
  ...
TypeError: A() missing 1 required keyword-only argument: 'a'
>>> A(a=1)
A(a=1)

>>> @define
... class C:
...     _x: int = field(init=False, default=42)
>>> C()
C(_x=42)
>>> C(23)
Traceback (most recent call last):
   ...
TypeError: __init__() takes exactly 1 argument (2 given)
1 cũng có thể được chỉ định tại Via
>>> @define
... class C:
...     _x: int = field(init=False, default=42)
>>> C()
C(_x=42)
>>> C(23)
Traceback (most recent call last):
   ...
TypeError: __init__() takes exactly 1 argument (2 given)
2 và sẽ áp dụng cho tất cả các thuộc tính:

>>> @define(kw_only=True)
... class A:
...     a: int
...     b: int
>>> A(1, 2)
Traceback (most recent call last):
  ...
TypeError: __init__() takes 1 positional argument but 3 were given
>>> A(a=1, b=2)
A(a=1, b=2)

Nếu bạn tạo một thuộc tính với

>>> @define
... class C:
...     _x: int = field(init=False, default=42)
>>> C()
C(_x=42)
>>> C(23)
Traceback (most recent call last):
   ...
TypeError: __init__() takes exactly 1 argument (2 given)
3, đối số
>>> @define
... class C:
...     _x: int = field(init=False, default=42)
>>> C()
C(_x=42)
>>> C(23)
Traceback (most recent call last):
   ...
TypeError: __init__() takes exactly 1 argument (2 given)
1 sẽ bị bỏ qua.

Các thuộc tính chỉ từ khóa cho phép các lớp con thêm các thuộc tính mà không có giá trị mặc định, ngay cả khi lớp cơ sở xác định các thuộc tính có giá trị mặc định:

>>> @define
... class A:
...     a: int = 0
>>> @define
... class B(A):
...     b: int = field(kw_only=True)
>>> B(b=1)
B(a=0, b=1)
>>> B()
Traceback (most recent call last):
  ...
TypeError: B() missing 1 required keyword-only argument: 'b'

Nếu bạn không đặt

>>> @define
... class C:
...     _x: int = field(init=False, default=42)
>>> C()
C(_x=42)
>>> C(23)
Traceback (most recent call last):
   ...
TypeError: __init__() takes exactly 1 argument (2 given)
5, thì không có thứ tự thuộc tính hợp lệ nào và bạn sẽ gặp lỗi:

>>> @define
... class Coordinates:
...     x: int
...     y: int
0

Chuyển đổi sang các loại bộ sưu tập#

Khi bạn có một lớp có dữ liệu, thường rất thuận tiện khi biến lớp đó thành

>>> @define
... class C:
...     _x: int = field(init=False, default=42)
>>> C()
C(_x=42)
>>> C(23)
Traceback (most recent call last):
   ...
TypeError: __init__() takes exactly 1 argument (2 given)
6 (ví dụ: nếu bạn muốn tuần tự hóa nó thành JSON):

>>> @define
... class Coordinates:
...     x: int
...     y: int
1

Một số trường không thể hoặc không nên biến đổi. Vì vậy,

>>> @define
... class C:
...     _x: int = field(init=False, default=42)
>>> C()
C(_x=42)
>>> C(23)
Traceback (most recent call last):
   ...
TypeError: __init__() takes exactly 1 argument (2 given)
7 cung cấp một cuộc gọi lại quyết định liệu có nên bao gồm một thuộc tính hay không:

>>> @define
... class Coordinates:
...     x: int
...     y: int
2

Đối với trường hợp phổ biến mà bạn muốn

>>> @define
... class C:
...     _x: int = field(init=False, default=42)
>>> C()
C(_x=42)
>>> C(23)
Traceback (most recent call last):
   ...
TypeError: __init__() takes exactly 1 argument (2 given)
8 hoặc
>>> @define
... class C:
...     _x: int = field(init=False, default=42)
>>> C()
C(_x=42)
>>> C(23)
Traceback (most recent call last):
   ...
TypeError: __init__() takes exactly 1 argument (2 given)
9 một số loại hoặc thuộc tính nhất định,
>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 tàu với một vài người trợ giúp:

>>> @define
... class Coordinates:
...     x: int
...     y: int
3

Những lần khác, tất cả những gì bạn muốn là một tuple và

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 won đã làm bạn thất vọng:

>>> @define
... class Coordinates:
...     x: int
...     y: int
4

Để chuyển đổi và chuyển đổi nâng cao hơn, chúng tôi khuyên bạn nên xem một thư viện đồng hành (chẳng hạn như CATTRS).

Defaults#

Đôi khi bạn muốn có các giá trị mặc định cho trình khởi tạo của bạn. Và đôi khi bạn thậm chí muốn các đối tượng có thể thay đổi làm giá trị mặc định (từng vô tình sử dụng

>>> class SomethingFromSomeoneElse:
...     def __init__(self, x):
...         self.x = x
>>> SomethingFromSomeoneElse = define(
...     these={
...         "x": field()
...     }, init=False)(SomethingFromSomeoneElse)
>>> SomethingFromSomeoneElse(1)
SomethingFromSomeoneElse(x=1)
2?).
>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 Bạn đã bảo hiểm trong cả hai trường hợp:

>>> @define
... class Coordinates:
...     x: int
...     y: int
5

Thông tin thêm về lý do tại sao các phương thức lớp để xây dựng các đối tượng là tuyệt vời có thể được tìm thấy trong bài đăng blog sâu sắc này.

Các nhà máy mặc định cũng có thể được đặt bằng đối số

>>> class SomethingFromSomeoneElse:
...     def __init__(self, x):
...         self.x = x
>>> SomethingFromSomeoneElse = define(
...     these={
...         "x": field()
...     }, init=False)(SomethingFromSomeoneElse)
>>> SomethingFromSomeoneElse(1)
SomethingFromSomeoneElse(x=1)
4 thành
>>> class SomethingFromSomeoneElse:
...     def __init__(self, x):
...         self.x = x
>>> SomethingFromSomeoneElse = define(
...     these={
...         "x": field()
...     }, init=False)(SomethingFromSomeoneElse)
>>> SomethingFromSomeoneElse(1)
SomethingFromSomeoneElse(x=1)
5 và sử dụng một bộ trang trí. Phương thức nhận được phiên bản khởi tạo một phần cho phép bạn dựa trên giá trị mặc định trên các thuộc tính khác:

>>> @define
... class Coordinates:
...     x: int
...     y: int
6

Xin lưu ý rằng cách tiếp cận trang trí chỉ hoạt động nếu thuộc tính trong câu hỏi có

>>> class SomethingFromSomeoneElse:
...     def __init__(self, x):
...         self.x = x
>>> SomethingFromSomeoneElse = define(
...     these={
...         "x": field()
...     }, init=False)(SomethingFromSomeoneElse)
>>> SomethingFromSomeoneElse(1)
SomethingFromSomeoneElse(x=1)
5 được gán cho nó. Do đó, việc chú thích một thuộc tính có loại là không đủ nếu bạn sử dụng
>>> class SomethingFromSomeoneElse:
...     def __init__(self, x):
...         self.x = x
>>> SomethingFromSomeoneElse = define(
...     these={
...         "x": field()
...     }, init=False)(SomethingFromSomeoneElse)
>>> SomethingFromSomeoneElse(1)
SomethingFromSomeoneElse(x=1)
7.

Validators#

Mặc dù các bộ khởi tạo của bạn nên làm ít nhất có thể (lý tưởng nhất: chỉ cần khởi tạo thể hiện của bạn theo các đối số!), Nó có thể có ích để thực hiện một số loại xác thực trên các đối số.

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 cung cấp hai cách để xác định trình xác nhận cho từng thuộc tính và bạn sẽ chọn một cách phù hợp với phong cách của bạn và dự án tốt hơn.

Bạn có thể sử dụng một người trang trí:

>>> @define
... class Coordinates:
...     x: int
...     y: int
7

… Hoặc một người có thể gọi được

>>> @define
... class Coordinates:
...     x: int
...     y: int
8

… Hoặc cả hai cùng một lúc:

>>> @define
... class Coordinates:
...     x: int
...     y: int
9

Xin lưu ý rằng cách tiếp cận trang trí chỉ hoạt động nếu - và chỉ khi! - Thuộc tính trong câu hỏi có

>>> class SomethingFromSomeoneElse:
...     def __init__(self, x):
...         self.x = x
>>> SomethingFromSomeoneElse = define(
...     these={
...         "x": field()
...     }, init=False)(SomethingFromSomeoneElse)
>>> SomethingFromSomeoneElse(1)
SomethingFromSomeoneElse(x=1)
5 được gán. Do đó, nếu bạn sử dụng
>>> @define(slots=False)
... class A:
...     a: int
...     def get_a(self):
...         return self.a
>>> @define(slots=False)
... class B:
...     b: int
>>> @define(slots=False)
... class C(B, A):
...     c: int
>>> i = C(1, 2, 3)
>>> i
C(a=1, b=2, c=3)
>>> i == C(1, 2, 3)
True
>>> i.get_a()
1
0, việc chú thích thuộc tính đã nói bằng một loại là không đủ.

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 vận chuyển với một loạt các trình xác nhận, hãy đảm bảo kiểm tra chúng trước khi viết của riêng bạn:check them out before writing your own:

>>> c1 = Coordinates(1, 2)
>>> c1
Coordinates(x=1, y=2)
>>> c2 = Coordinates(x=2, y=1)
>>> c2
Coordinates(x=2, y=1)
>>> c1 == c2
False
0

Xin lưu ý rằng nếu bạn sử dụng

>>> @define(slots=False)
... class A:
...     a: int
...     def get_a(self):
...         return self.a
>>> @define(slots=False)
... class B:
...     b: int
>>> @define(slots=False)
... class C(B, A):
...     c: int
>>> i = C(1, 2, 3)
>>> i
C(a=1, b=2, c=3)
>>> i == C(1, 2, 3)
True
>>> i.get_a()
1
2 (chứ không phải
>>> @define(slots=False)
... class A:
...     a: int
...     def get_a(self):
...         return self.a
>>> @define(slots=False)
... class B:
...     b: int
>>> @define(slots=False)
... class C(B, A):
...     c: int
>>> i = C(1, 2, 3)
>>> i
C(a=1, b=2, c=3)
>>> i == C(1, 2, 3)
True
>>> i.get_a()
1
3) để xác định lớp của bạn, trình xác nhận chỉ chạy khi khởi tạo theo mặc định. Hành vi này có thể được thay đổi bằng đối số
>>> @define(slots=False)
... class A:
...     a: int
...     def get_a(self):
...         return self.a
>>> @define(slots=False)
... class B:
...     b: int
>>> @define(slots=False)
... class C(B, A):
...     c: int
>>> i = C(1, 2, 3)
>>> i
C(a=1, b=2, c=3)
>>> i == C(1, 2, 3)
True
>>> i.get_a()
1
4.

Kiểm tra trình xác nhận để biết thêm chi tiết.Validators for more details.

Conversion#

Các thuộc tính có thể có chức năng

>>> @define(slots=False)
... class A:
...     a: int
...     def get_a(self):
...         return self.a
>>> @define(slots=False)
... class B:
...     b: int
>>> @define(slots=False)
... class C(B, A):
...     c: int
>>> i = C(1, 2, 3)
>>> i
C(a=1, b=2, c=3)
>>> i == C(1, 2, 3)
True
>>> i.get_a()
1
5 được chỉ định, sẽ được gọi với giá trị truyền thuộc tính để có được giá trị mới để sử dụng. Điều này có thể hữu ích để thực hiện các phần chuyển đổi loại trên các giá trị mà bạn không muốn buộc người gọi của bạn phải làm.

>>> c1 = Coordinates(1, 2)
>>> c1
Coordinates(x=1, y=2)
>>> c2 = Coordinates(x=2, y=1)
>>> c2
Coordinates(x=2, y=1)
>>> c1 == c2
False
1

Xin lưu ý rằng các bộ chuyển đổi chỉ chạy khi khởi tạo.

Kiểm tra bộ chuyển đổi để biết thêm chi tiết.Converters for more details.

Metadata#

Tất cả các thuộc tính

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 có thể bao gồm siêu dữ liệu tùy ý dưới dạng từ điển chỉ đọc.

>>> c1 = Coordinates(1, 2)
>>> c1
Coordinates(x=1, y=2)
>>> c2 = Coordinates(x=2, y=1)
>>> c2
Coordinates(x=2, y=1)
>>> c1 == c2
False
2

Siêu dữ liệu không được sử dụng bởi

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 và có nghĩa là để cho phép chức năng phong phú trong các thư viện của bên thứ ba. Từ điển siêu dữ liệu tuân theo các quy tắc từ điển thông thường: các khóa cần phải được băm, và cả các khóa và giá trị được khuyến nghị là bất biến.

Nếu bạn là tác giả của thư viện bên thứ ba với tích hợp

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5, vui lòng xem siêu dữ liệu mở rộng.Extending Metadata.

Types#

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 cũng cho phép bạn liên kết một loại với một thuộc tính bằng cách sử dụng đối số loại với
>>> @define
... class A:
...     a: int = field(kw_only=True)
>>> A()
Traceback (most recent call last):
  ...
TypeError: A() missing 1 required keyword-only argument: 'a'
>>> A(a=1)
A(a=1)
0 hoặc-kể từ Python 3.6-sử dụng PEP 526-Renotations:PEP 526-annotations:

>>> c1 = Coordinates(1, 2)
>>> c1
Coordinates(x=1, y=2)
>>> c2 = Coordinates(x=2, y=1)
>>> c2
Coordinates(x=2, y=1)
>>> c1 == c2
False
3

Nếu bạn không quan tâm đến việc chú thích tất cả các thuộc tính, bạn thậm chí có thể bỏ

>>> @define
... class A:
...     a: int = field(kw_only=True)
>>> A()
Traceback (most recent call last):
  ...
TypeError: A() missing 1 required keyword-only argument: 'a'
>>> A(a=1)
A(a=1)
1 và gán các giá trị mặc định thay thế:

>>> c1 = Coordinates(1, 2)
>>> c1
Coordinates(x=1, y=2)
>>> c2 = Coordinates(x=2, y=1)
>>> c2
Coordinates(x=2, y=1)
>>> c1 == c2
False
4

Phương thức

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
7 được tạo sẽ có một thuộc tính gọi là
>>> @define
... class A:
...     a: int = field(kw_only=True)
>>> A()
Traceback (most recent call last):
  ...
TypeError: A() missing 1 required keyword-only argument: 'a'
>>> A(a=1)
A(a=1)
3 chứa thông tin loại này.

Nếu các chú thích của bạn chứa các chuỗi (ví dụ: tham chiếu chuyển tiếp), bạn có thể giải quyết chúng sau khi tất cả các tài liệu tham khảo đã được xác định bằng cách sử dụng

>>> @define
... class A:
...     a: int = field(kw_only=True)
>>> A()
Traceback (most recent call last):
  ...
TypeError: A() missing 1 required keyword-only argument: 'a'
>>> A(a=1)
A(a=1)
4. Điều này sẽ thay thế thuộc tính loại trong các trường tương ứng.

>>> c1 = Coordinates(1, 2)
>>> c1
Coordinates(x=1, y=2)
>>> c2 = Coordinates(x=2, y=1)
>>> c2
Coordinates(x=2, y=1)
>>> c1 == c2
False
5

Ghi chú

Nếu bạn thấy mình sử dụng các chú thích loại chuỗi để xử lý các tài liệu tham khảo phía trước, hãy kết thúc toàn bộ chú thích loại trong trích dẫn thay vì chỉ loại bạn cần tham chiếu chuyển tiếp (vì vậy

>>> @define
... class A:
...     a: int = field(kw_only=True)
>>> A()
Traceback (most recent call last):
  ...
TypeError: A() missing 1 required keyword-only argument: 'a'
>>> A(a=1)
A(a=1)
5 thay vì
>>> @define
... class A:
...     a: int = field(kw_only=True)
>>> A()
Traceback (most recent call last):
  ...
TypeError: A() missing 1 required keyword-only argument: 'a'
>>> A(a=1)
A(a=1)
6). Đây là một hạn chế của hệ thống gõ Python.

Cảnh báo

Bản thân

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 không có bất kỳ tính năng nào hoạt động trên loại siêu dữ liệu loại. Tuy nhiên, nó rất hữu ích cho việc viết các trình xác nhận hoặc khung tuần tự hóa của riêng bạn.

Slots#

Các lớp có rãnh có một số lợi thế trên Cpython. Xác định

>>> @define
... class A:
...     a: int = field(kw_only=True)
>>> A()
Traceback (most recent call last):
  ...
TypeError: A() missing 1 required keyword-only argument: 'a'
>>> A(a=1)
A(a=1)
8 bằng tay là tẻ nhạt, trong
>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5, nó chỉ là vấn đề sử dụng
>>> @define(slots=False)
... class A:
...     a: int
...     def get_a(self):
...         return self.a
>>> @define(slots=False)
... class B:
...     b: int
>>> @define(slots=False)
... class C(B, A):
...     c: int
>>> i = C(1, 2, 3)
>>> i
C(a=1, b=2, c=3)
>>> i == C(1, 2, 3)
True
>>> i.get_a()
1
3 hoặc chuyển
>>> @define(kw_only=True)
... class A:
...     a: int
...     b: int
>>> A(1, 2)
Traceback (most recent call last):
  ...
TypeError: __init__() takes 1 positional argument but 3 were given
>>> A(a=1, b=2)
A(a=1, b=2)
1 đến
>>> @define(slots=False)
... class A:
...     a: int
...     def get_a(self):
...         return self.a
>>> @define(slots=False)
... class B:
...     b: int
>>> @define(slots=False)
... class C(B, A):
...     c: int
>>> i = C(1, 2, 3)
>>> i
C(a=1, b=2, c=3)
>>> i == C(1, 2, 3)
True
>>> i.get_a()
1
2: have several advantages on CPython. Defining
>>> @define
... class A:
...     a: int = field(kw_only=True)
>>> A()
Traceback (most recent call last):
  ...
TypeError: A() missing 1 required keyword-only argument: 'a'
>>> A(a=1)
A(a=1)
8 by hand is tedious, in
>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 it’s just a matter of using
>>> @define(slots=False)
... class A:
...     a: int
...     def get_a(self):
...         return self.a
>>> @define(slots=False)
... class B:
...     b: int
>>> @define(slots=False)
... class C(B, A):
...     c: int
>>> i = C(1, 2, 3)
>>> i
C(a=1, b=2, c=3)
>>> i == C(1, 2, 3)
True
>>> i.get_a()
1
3 or passing
>>> @define(kw_only=True)
... class A:
...     a: int
...     b: int
>>> A(1, 2)
Traceback (most recent call last):
  ...
TypeError: __init__() takes 1 positional argument but 3 were given
>>> A(a=1, b=2)
A(a=1, b=2)
1 to
>>> @define(slots=False)
... class A:
...     a: int
...     def get_a(self):
...         return self.a
>>> @define(slots=False)
... class B:
...     b: int
>>> @define(slots=False)
... class C(B, A):
...     c: int
>>> i = C(1, 2, 3)
>>> i
C(a=1, b=2, c=3)
>>> i == C(1, 2, 3)
True
>>> i.get_a()
1
2:

>>> c1 = Coordinates(1, 2)
>>> c1
Coordinates(x=1, y=2)
>>> c2 = Coordinates(x=2, y=1)
>>> c2
Coordinates(x=2, y=1)
>>> c1 == c2
False
6

Immutability#

Đôi khi bạn có những trường hợp không nên thay đổi sau khi khởi tạo. Sự bất biến đặc biệt phổ biến trong lập trình chức năng và nói chung là một điều rất tốt. Nếu bạn muốn thực thi nó,

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 sẽ cố gắng giúp đỡ:

>>> c1 = Coordinates(1, 2)
>>> c1
Coordinates(x=1, y=2)
>>> c2 = Coordinates(x=2, y=1)
>>> c2
Coordinates(x=2, y=1)
>>> c1 == c2
False
7

Xin lưu ý rằng tính bất biến thực sự là không thể trong Python nhưng nó sẽ giúp bạn có 99% ở đó. Theo bản thân họ, các lớp bất biến là hữu ích cho các đối tượng tồn tại lâu dài không bao giờ thay đổi; Giống như cấu hình ví dụ.get you 99% there. By themselves, immutable classes are useful for long-lived objects that should never change; like configurations for example.

Để sử dụng chúng trong luồng chương trình thông thường, bạn sẽ cần một cách để dễ dàng tạo các phiên bản mới với các thuộc tính đã thay đổi. Trong Clojure, chức năng được gọi là PGS và

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 không biết xấu hổ bắt chước nó:
>>> @define(kw_only=True)
... class A:
...     a: int
...     b: int
>>> A(1, 2)
Traceback (most recent call last):
  ...
TypeError: __init__() takes 1 positional argument but 3 were given
>>> A(a=1, b=2)
A(a=1, b=2)
5:

>>> c1 = Coordinates(1, 2)
>>> c1
Coordinates(x=1, y=2)
>>> c2 = Coordinates(x=2, y=1)
>>> c2
Coordinates(x=2, y=1)
>>> c1 == c2
False
8

Những goodies khác#

Đôi khi bạn có thể muốn tạo một lớp theo chương trình.

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
5 won cho phép bạn thất vọng và cho bạn
>>> @define(kw_only=True)
... class A:
...     a: int
...     b: int
>>> A(1, 2)
Traceback (most recent call last):
  ...
TypeError: __init__() takes 1 positional argument but 3 were given
>>> A(a=1, b=2)
A(a=1, b=2)
7:

>>> c1 = Coordinates(1, 2)
>>> c1
Coordinates(x=1, y=2)
>>> c2 = Coordinates(x=2, y=1)
>>> c2
Coordinates(x=2, y=1)
>>> c1 == c2
False
9

Bạn vẫn có thể có quyền lực đối với các thuộc tính nếu bạn vượt qua từ điển tên:

>>> class SomethingFromSomeoneElse:
...     def __init__(self, x):
...         self.x = x
>>> SomethingFromSomeoneElse = define(
...     these={
...         "x": field()
...     }, init=False)(SomethingFromSomeoneElse)
>>> SomethingFromSomeoneElse(1)
SomethingFromSomeoneElse(x=1)
5 ánh xạ và có thể chuyển các đối số sang
>>> @define(kw_only=True)
... class A:
...     a: int
...     b: int
>>> A(1, 2)
Traceback (most recent call last):
  ...
TypeError: __init__() takes 1 positional argument but 3 were given
>>> A(a=1, b=2)
A(a=1, b=2)
9:

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
0

Nếu bạn cần tự động tạo một lớp với

>>> @define(kw_only=True)
... class A:
...     a: int
...     b: int
>>> A(1, 2)
Traceback (most recent call last):
  ...
TypeError: __init__() takes 1 positional argument but 3 were given
>>> A(a=1, b=2)
A(a=1, b=2)
7 và nó cần phải là một lớp con của một thứ khác ngoài
>>> @define
... class A:
...     a: int = 0
>>> @define
... class B(A):
...     b: int = field(kw_only=True)
>>> B(b=1)
B(a=0, b=1)
>>> B()
Traceback (most recent call last):
  ...
TypeError: B() missing 1 required keyword-only argument: 'b'
1, hãy sử dụng đối số
>>> @define
... class A:
...     a: int = 0
>>> @define
... class B(A):
...     b: int = field(kw_only=True)
>>> B(b=1)
B(a=0, b=1)
>>> B()
Traceback (most recent call last):
  ...
TypeError: B() missing 1 required keyword-only argument: 'b'
2:

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
1

Đôi khi, bạn muốn có phương thức lớp ____ ____37 của bạn không chỉ làm việc không chỉ là khởi tạo, xác thực, v.v. được thực hiện cho bạn một cách tự động khi sử dụng

>>> @define
... class A:
...     a: int = 0
>>> @define
... class B(A):
...     b: int = field(kw_only=True)
>>> B(b=1)
B(a=0, b=1)
>>> B()
Traceback (most recent call last):
  ...
TypeError: B() missing 1 required keyword-only argument: 'b'
4. Để làm điều này, chỉ cần xác định một phương thức
>>> @define
... class A:
...     a: int = 0
>>> @define
... class B(A):
...     b: int = field(kw_only=True)
>>> B(b=1)
B(a=0, b=1)
>>> B()
Traceback (most recent call last):
  ...
TypeError: B() missing 1 required keyword-only argument: 'b'
5 trong lớp của bạn. Nó sẽ được gọi vào cuối phương thức
>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
7 được tạo.

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
2

Bạn có thể loại trừ các thuộc tính đơn từ một số phương thức nhất định:

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
3

Ngoài ra, để ảnh hưởng đến cách phương thức

>>> @define
... class A:
...     a: int = 0
>>> @define
... class B(A):
...     b: int = field(kw_only=True)
>>> B(b=1)
B(a=0, b=1)
>>> B()
Traceback (most recent call last):
  ...
TypeError: B() missing 1 required keyword-only argument: 'b'
7 được tạo định dạng một thuộc tính cụ thể, chỉ định một tùy chỉnh có thể gọi để được sử dụng thay vì hàm tích hợp
>>> @define
... class A:
...     a: int = 0
>>> @define
... class B(A):
...     b: int = field(kw_only=True)
>>> B(b=1)
B(a=0, b=1)
>>> B()
Traceback (most recent call last):
  ...
TypeError: B() missing 1 required keyword-only argument: 'b'
8:

>>> @define
... class C:
...     _x: int
>>> C(x=1)
C(_x=1)
4