44. Lớp học và siêu dữ liệu
44. Lớp và siêu lớp
Một lớp là một đối tượng thời gian chạy tạo ra các thể hiện, lưu trữ các thuộc tính, tham gia kế thừa và xác định hành vi thông qua mô hình đối tượng Python. Trong CPython, một lớp là một đối tượng có kiểu thường làtype.
Siêu dữ liệu là lớp của một lớp. Nó kiểm soát cách các đối tượng lớp được tạo, khởi tạo, biểu diễn và gọi.
Đối với lớp học bình thường:```python id="g3fkzi" class User: pass
print(type(User))
Đầu ra:text id="iqt3cc"
<class 'type'>
```Điều này có nghĩaUserlà một đối tượng và loại của nó làtype.
44.1 Lớp là đối tượng
Một định nghĩa lớp tạo ra một đối tượng lớp.```python id="8zc5m6" class User: name = "anonymous"
def hello(self):
return "hello"
Sau khi thực hiện,`User`là một tên bình thường bị ràng buộc trong không gian tên xung quanh.python id="rx6c1m"
print(User)
print(type(User))
print(User.name)
print(User.dict)
Đối tượng lớp lưu trữ các thuộc tính như:text id="xxewj0"
name
qualname
module
dict
bases
mro
methods
descriptors
class variables
annotations
## 44.2 Định nghĩa lớp là thực thi
Thân lớp là mã thực thi được.```python id="938wkx"
class Example:
print("inside class body")
x = 1 + 2
```Đầu ra trong quá trình định nghĩa:```text id="jiq7he"
inside class body
```Nội dung lớp chạy ngay lập tức khi CPython thực thi lệnh`class`tuyên bố. Nó không bị trì hoãn cho đến khi một thể hiện được tạo ra.
Điều này quan trọng vì mọi mã cấp cao nhất bên trong nội dung lớp đều chạy vào thời điểm tạo lớp:```python id="aj5kyg"
class Bad:
data = load_large_file()
```Công việc đó xảy ra khi lớp được xác định.
## 44.3 Đường dẫn tạo lớp
Một định nghĩa lớp:```python id="yzw9ss"
class User(Base):
x = 1
def hello(self):
return "hello"
```về mặt khái niệm tương tự như:```python id="g55w7a"
namespace = {}
namespace["x"] = 1
namespace["hello"] = function_object
User = type("User", (Base,), namespace)
```Quá trình thực tế có nhiều bước hơn:```text id="zd6xb1"
1. Evaluate base classes.
2. Determine the metaclass.
3. Ask the metaclass for a class namespace.
4. Execute the class body in that namespace.
5. Create the class object.
6. Call descriptor __set_name__ methods.
7. Call subclass initialization hooks.
8. Bind the class object to its name.
```Quy trình này giải thích cách siêu lớp, bộ mô tả, bộ trang trí và tính kế thừa tương tác với nhau như thế nào.
## 44.4 Đánh giá các lớp cơ sở
Trong:```python id="zu9u35"
class User(Model):
pass
```CPython đánh giá đầu tiên`Model`.
Các lớp cơ sở là các biểu thức:```python id="fnc7gv"
class User(get_base_class()):
pass
```Cuộc gọi hàm xảy ra trước khi đối tượng lớp được tạo.
Nhiều cơ sở được đánh giá từ trái sang phải:```python id="qtk75l"
class C(A(), B()):
pass
```Các đối tượng kết quả phải là các lớp cơ sở hợp lệ hoặc có thể chuyển đổi thông qua`__mro_entries__`.
## 44,5`__mro_entries__`Cái móc`__mro_entries__`cho phép các đối tượng cơ sở không thuộc lớp tự thay thế chúng trong quá trình tạo lớp.
Điều này được sử dụng bởi một số máy đánh máy và máy móc chung.
Hình dạng ví dụ:```python id="esjxp2"
class BaseAlias:
def __mro_entries__(self, bases):
return (RealBase,)
class C(BaseAlias()):
pass
```Về mặt khái niệm, CPython biến:```python id="154gd3"
class C(BaseAlias()):
pass
```vào trong:```python id="kfk2ta"
class C(RealBase):
pass
```nhằm mục đích thừa kế.
Hầu hết mã ứng dụng không bao giờ triển khai`__mro_entries__`, nhưng nó là một phần của việc tạo lớp.
## 44.6 Xác định Metaclass
Siêu dữ liệu có thể được chỉ định rõ ràng:```python id="o3wmta"
class User(metaclass=Meta):
pass
```Nếu không có siêu dữ liệu nào được chỉ định, CPython sẽ lấy nó từ các lớp cơ sở.
Đối với lớp học bình thường:```python id="oe50zn"
class User:
pass
```siêu dữ liệu là:```text id="ythfx3"
type
```Đối với một lớp con:```python id="t5oe3k"
class Child(Base):
pass
```siêu dữ liệu thường là`type(Base)`hoặc một siêu dữ liệu dẫn xuất tương thích.
Siêu dữ liệu được chọn phải tương thích với siêu dữ liệu của tất cả các lớp cơ sở. Nếu không thì CPython sẽ gây ra xung đột siêu dữ liệu.
## 44.7 Xung đột siêu lớp
Xung đột siêu dữ liệu xảy ra khi các lớp cơ sở yêu cầu siêu dữ liệu không tương thích.
Ví dụ:```python id="dk85qy"
class MetaA(type):
pass
class MetaB(type):
pass
class A(metaclass=MetaA):
pass
class B(metaclass=MetaB):
pass
class C(A, B):
pass
```Điều này gây ra lỗi vì CPython không thể chọn một siêu dữ liệu duy nhất tương thích với cả hai`MetaA`Và`MetaB`.
Cách khắc phục thông thường là xác định một siêu dữ liệu kết hợp:```python id="7bm0x5"
class MetaC(MetaA, MetaB):
pass
class C(A, B, metaclass=MetaC):
pass
```Xung đột siêu dữ liệu là phổ biến khi kết hợp các khung sử dụng siêu dữ liệu.
## 44.8 Chuẩn bị không gian tên lớp
Trước khi thực thi nội dung lớp, CPython yêu cầu siêu dữ liệu cung cấp một không gian tên.
Nó thực hiện điều này bằng cách gọi`__prepare__`nếu có mặt.```python id="0o8dg6"
class Meta(type):
@classmethod
def __prepare__(mcls, name, bases, **kwargs):
return {}
class User(metaclass=Meta):
x = 1
```Đối tượng trả về được sử dụng làm không gian tên cục bộ cho nội dung lớp.
Về mặt lịch sử, điều này cho phép các không gian tên lớp được sắp xếp. Từ điển hiện đại giữ nguyên thứ tự chèn, nhưng`__prepare__`vẫn hỗ trợ hành vi không gian tên tùy chỉnh.
Các trường hợp sử dụng ví dụ:```text id="rq5a86"
tracking declaration order
rejecting duplicate names
collecting field definitions
custom class DSLs
framework model declarations
```## 44.9 Không gian tên nội dung lớp
Thân lớp thực thi với không gian tên cục bộ của chính nó.```python id="4q8zlf"
x = "global"
class Example:
x = "class local"
y = x
print(Example.x)
print(Example.y)
```Đầu ra:```text id="7d4l4l"
class local
class local
```Các bài tập trong nội dung lớp ghi vào vùng tên lớp chứ không phải vào một thể hiện.
Các thân hàm bên trong lớp không tự động ghi lại tên cục bộ của lớp:```python id="kkxro6"
class Example:
x = 10
def method(self):
return x
```Điều này thường thất bại trong thời gian chạy trừ khi có một toàn cục`x`, bởi vì phương thức tra cứu toàn cục sử dụng toàn cục mô-đun chứ không phải không gian tên lớp.
Chính xác:```python id="mjh0gx"
class Example:
x = 10
def method(self):
return self.x
```hoặc:```python id="73a9fm"
class Example:
x = 10
def method(self):
return type(self).x
```## 44.10 Tạo đối tượng lớp
Sau khi thực thi phần thân lớp, CPython gọi siêu dữ liệu.
Đối với một lớp bình thường, điều này có nghĩa là gọi`type`.
Về mặt khái niệm:```python id="y5jzez"
User = type("User", bases, namespace)
```Cuộc gọi đến`type`tạo ra một`PyTypeObject`nội bộ.
Đối tượng lớp kết quả lưu trữ:```text id="q6gq90"
class name
base classes
method resolution order
class dictionary
type flags
slot tables
weakref support
instance layout
descriptor information
subclass relationships
```Do đó, một lớp do người dùng định nghĩa là một đối tượng kiểu.
## 44.11`type(name, bases, namespace)`Bạn có thể tạo các lớp theo cách thủ công với`type`.
```python id="swqmpu"
def hello(self):
return "hello"
User = type("User", (), {"hello": hello})
u = User()
print(u.hello())
```Điều này tương đương về mặt tinh thần với:```python id="owh4rx"
class User:
def hello(self):
return "hello"
```các`class`câu lệnh là cú pháp cho một giao thức tạo lớp có cấu trúc.
## 44.12 Siêu lớp`__new__`Siêu dữ liệu có thể tùy chỉnh việc tạo lớp bằng cách ghi đè`__new__`.
```python id="55v7xr"
class Meta(type):
def __new__(mcls, name, bases, namespace, **kwargs):
print("creating", name)
return super().__new__(mcls, name, bases, namespace)
class User(metaclass=Meta):
pass
```Đầu ra:```text id="xsdbla"
creating User
__new__nhận tên lớp, lớp cơ sở và không gian tên trước khi đối tượng lớp tồn tại.
Sử dụng__new__khi bạn cần thay đổi lớp trước khi tạo:text id="e80nxe" modify namespace validate definitions inject methods collect metadata change base classes control class object allocation ## 44.13 Siêu lớp__init__Siêu dữ liệu có thể tùy chỉnh việc khởi tạo lớp bằng cách ghi đè__init__.
class Meta(type):
def __init__(cls, name, bases, namespace, **kwargs):
print("initializing", name)
super().__init__(name, bases, namespace)
class User(metaclass=Meta):
pass
__init__nhận đối tượng lớp đã được tạo dưới dạngcls.
Sử dụng siêu dữ liệu__init__khi bạn cần đăng ký hoặc kiểm tra lớp sau khi tạo:text id="u1q6kk" register subclasses validate final class attach metadata update external registries ## 44.14 Siêu lớp__call__Việc gọi một lớp được điều khiển bởi siêu dữ liệu của nó.
Đối với lớp học bình thường:python id="kq4rc6" u = User("Ada") được xử lý bởi:text id="y1lk7w" type(User).__call__(User, "Ada") Đối với siêu dữ liệu bình thường,type.__call__thực hiện:```text id="vxizzk"
- call User.new(User, ...)
- if result is an instance of User, call User.init(instance, ...)
- return instance
```Một siêu dữ liệu có thể ghi đè
__call__:
class SingletonMeta(type):
def __call__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
cls._instance = super().__call__(*args, **kwargs)
return cls._instance
class Config(metaclass=SingletonMeta):
pass
```Bây giờ mỗi`Config()`cuộc gọi trả về cùng một đối tượng.
Sử dụng điều này một cách tiết kiệm. Siêu lớp`__call__`thay đổi việc tạo cá thể trên toàn cầu cho lớp.
## 44.15 Tạo phiên bản
Đối với lớp học bình thường:```python id="6kcvu5"
class User:
def __new__(cls, name):
print("__new__")
return super().__new__(cls)
def __init__(self, name):
print("__init__")
self.name = name
u = User("Ada")
```Đầu ra:```text id="868s0q"
__new__
__init__
__new__tạo ra đối tượng.__init__khởi tạo đối tượng.__new__là một hàm tạo giống như tĩnh. Nó nhận lớp và trả về một đối tượng.__init__nhận được phiên bản đã tạo và sẽ trả vềNone.
44.16 Từ điển lớp
Một lớp hiển thị không gian tên của nó thông qua__dict__.
class User:
kind = "human"
def hello(self):
return "hello"
print(User.__dict__)
User.__dict__thường là proxy ánh xạ chỉ đọc.python id="pj2w2d" print(type(User.__dict__)) Bạn không thể gán trực tiếp vào proxy ánh xạ:python id="sgmmu2" User.__dict__["x"] = 1 Nhưng bạn có thể gán thuộc tính cho lớp:```python id="4utuvq"
User.x = 1
## 44.17 Biến lớp
Một biến lớp được lưu trữ trên đối tượng lớp.```python id="hkk4e1"
class Counter:
count = 0
```Truy cập thông qua lớp:```python id="kts1o0"
print(Counter.count)
```Truy cập thông qua một thể hiện:```python id="8w340h"
c = Counter()
print(c.count)
```Nếu instance không có`count`, tra cứu tìm thấy nó trên lớp.
Việc gán thông qua phiên bản sẽ tạo hoặc cập nhật một thuộc tính phiên bản:```python id="d1dq8x"
c.count = 10
print(c.__dict__)
print(Counter.count)
```Đầu ra:```text id="frwv5a"
{'count': 10}
0
```Đây là nguồn lỗi phổ biến với các biến lớp có thể thay đổi.
## 44.18 Cạm bẫy biến đổi lớp có thể thay đổi```python id="x8s3te"
class Bag:
items = []
def add(self, item):
self.items.append(item)
```Cách sử dụng:```python id="81dg5u"
a = Bag()
b = Bag()
a.add("x")
print(b.items)
```Đầu ra:```text id="kqxpp6"
['x']
```Cả hai phiên bản đều có chung danh sách cấp lớp.
Thiết kế đúng:```python id="l9yqqx"
class Bag:
def __init__(self):
self.items = []
def add(self, item):
self.items.append(item)
```Sử dụng các biến lớp cho các hằng chia sẻ hoặc trạng thái chia sẻ có chủ ý. Sử dụng các biến mẫu cho trạng thái của từng phiên bản.
## 44.19 Từ điển sơ thẩm
Các đối tượng do người dùng định nghĩa thông thường có một từ điển mẫu.```python id="8j4hxd"
class User:
pass
u = User()
u.name = "Ada"
print(u.__dict__)
```Đầu ra:```text id="e5beij"
{'name': 'Ada'}
```Việc gán thuộc tính lưu trữ các giá trị trong từ điển mẫu trừ khi bộ mô tả dữ liệu chặn việc gán.
Đây là lý do tại sao các đối tượng Python lại linh hoạt theo mặc định. Các thuộc tính mới có thể được thêm động.
## 44,20`__slots__`và bố cục sơ thẩm
Một lớp có thể định nghĩa`__slots__`để sử dụng các vị trí thuộc tính cố định thay vì từ điển phiên bản thông thường.```python id="ok0zm7"
class Point:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
```Trường hợp của`Point`cửa hàng`x`Và`y`trong các khe cố định.```python id="3bbvk2"
p = Point(1, 2)
print(hasattr(p, "__dict__"))
```Thường xuyên:```text id="mk5rwc"
False
```Các khe tạo mô tả trên lớp:```python id="1l1jy7"
print(Point.__dict__["x"])
print(Point.__dict__["y"])
```Những bộ mô tả này đọc và ghi các vị trí lưu trữ cố định.
## 44.21 Kế thừa
Một lớp có thể kế thừa từ một hoặc nhiều lớp cơ sở.```python id="9jq7to"
class Animal:
def speak(self):
return "..."
class Dog(Animal):
def speak(self):
return "woof"
```Lớp con lưu trữ các lớp cơ sở của nó trong`__bases__`.
```python id="s4yqfg"
print(Dog.__bases__)
```Thứ tự phân giải phương thức được lưu trữ trong`__mro__`.
```python id="zfn7t5"
print(Dog.__mro__)
```Tra cứu thuộc tính tuân theo MRO.
## 44.22 Phương thức Thứ tự giải quyết
Dành cho:```python id="z3o721"
class A:
def f(self):
return "A"
class B(A):
pass
class C(B):
pass
C().f()tìm thấyfTRONGAthông qua MRO.python id="km9ffo" print(C.__mro__) Hình dạng đầu ra:```text id="mvct9m"
(<class 'main.C'>, <class 'main.B'>, <class 'main.A'>, <class 'object'>)
## 44.23 Đa kế thừa
Python hỗ trợ đa kế thừa.```python id="vr4scb"
class A:
def f(self):
return "A"
class B:
def f(self):
return "B"
class C(A, B):
pass
print(C().f())
print(C.__mro__)
```Căn cứ ngoài cùng bên trái thường được ưu tiên, tuân theo quy tắc C3 MRO.
Đầu ra:```text id="njnz6d"
A
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
```Đa kế thừa rất mạnh mẽ nhưng nó đòi hỏi phải có thiết kế hợp tác khi các lớp chia sẻ các phương thức và đường dẫn khởi tạo.
##44.24 Hợp tác xã`super`
`super()`tuân theo MRO.```python id="xgd0er"
class A:
def f(self):
return "A"
class B(A):
def f(self):
return "B" + super().f()
class C(A):
def f(self):
return "C" + super().f()
class D(B, C):
def f(self):
return "D" + super().f()
print(D().f())
print(D.__mro__)
```Đầu ra:```text id="f6z3iu"
DBCA
super()không có nghĩa là “gọi cho bố mẹ tôi.” Nó có nghĩa là “tiếp tục MRO sau lớp hiện tại.”
Điều này là cần thiết trong đa kế thừa.
44.25 Trang trí lớp học
Trình trang trí lớp sẽ nhận đối tượng lớp sau khi nó được tạo.```python id="6z5a86" def register(cls): registry[cls.name] = cls return cls
registry = {}
@register
class User:
pass
Đại khái là thế này:python id="alod77"
class User:
pass
User = register(User)
## 44,26`__init_subclass__`Một lớp cơ sở có thể định nghĩa`__init_subclass__`để chạy mã khi nó được phân lớp.```python id="zfxjod"
class Model:
registry = []
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
Model.registry.append(cls)
class User(Model):
pass
class Post(Model):
pass
print(Model.registry)
```Điều này thường đơn giản hơn siêu dữ liệu để đăng ký lớp con.
Sử dụng`__init_subclass__`vì:```text id="yp6gp0"
subclass registration
subclass validation
default subclass configuration
lightweight framework hooks
```## 44,27`__set_name__`Trong quá trình tạo lớp
Sau khi tạo một lớp, CPython gọi`__set_name__`trên các bộ mô tả trong không gian tên lớp.```python id="5vzb6d"
class Field:
def __set_name__(self, owner, name):
print(owner, name)
class User:
id = Field()
name = Field()
```Hình dạng đầu ra:```text id="vzb7xd"
<class '__main__.User'> id
<class '__main__.User'> name
```Điều này cho phép các bộ mô tả khám phá tên lớp và thuộc tính mà chúng được gán.
Thứ tự là:```text id="4ow51f"
class body executes
class object is created
descriptor __set_name__ hooks run
__init_subclass__ hooks run on bases
class decorators run
class name is bound
```## 44.28 Siêu lớp so với Trang trí lớp so với`__init_subclass__`| Cơ chế | Chạy | Tốt nhất cho |
|---|---|---|
| Siêu lớp | Trong quá trình tạo lớp | Kiểm soát sâu việc tạo đối tượng lớp |
| Trang trí lớp học | Sau khi tạo lớp | Chuyển đổi hoặc đăng ký một lần |
|`__init_subclass__`| Khi lớp con được tạo | Móc lớp con điều khiển lớp cơ sở |
| Bộ mô tả`__set_name__`| Trong quá trình hoàn thiện lớp học | Khám phá tên trường |
Thích cơ chế đơn giản nhất để giải quyết vấn đề.
Đối với hầu hết các mã:```text id="wd9e73"
class decorator > __init_subclass__ > metaclass
```Siêu dữ liệu là hợp lý khi bạn cần tùy chỉnh việc chuẩn bị không gian tên, phân bổ lớp, các phương thức ở cấp độ siêu dữ liệu, cách gọi lớp hoặc thực thi các quy tắc trên toàn hệ thống phân cấp lớp.
## 44.29 Ví dụ về siêu dữ liệu: Thực thi các thuộc tính bắt buộc```python id="p13a12"
class RequireTableName(type):
def __new__(mcls, name, bases, namespace):
cls = super().__new__(mcls, name, bases, namespace)
if bases and not hasattr(cls, "table_name"):
raise TypeError(f"{name} must define table_name")
return cls
class Model(metaclass=RequireTableName):
pass
class User(Model):
table_name = "users"
```Siêu dữ liệu này xác nhận các lớp con.
Điều này cũng có thể được thực hiện với`__init_subclass__`, thường đơn giản hơn:```python id="8wx9md"
class Model:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
if not hasattr(cls, "table_name"):
raise TypeError(f"{cls.__name__} must define table_name")
```Sử dụng hook lớp cơ sở trừ khi siêu dữ liệu mang lại lợi thế cụ thể.
## 44.30 Ví dụ về siêu dữ liệu: Sổ đăng ký```python id="q07d17"
class RegistryMeta(type):
registry = {}
def __init__(cls, name, bases, namespace):
super().__init__(name, bases, namespace)
if name != "Base":
RegistryMeta.registry[name] = cls
class Base(metaclass=RegistryMeta):
pass
class User(Base):
pass
class Post(Base):
pass
print(RegistryMeta.registry)
```Điều này ghi lại các lớp con khi chúng được tạo ra.
Lại,`__init_subclass__`có thể là đủ:```python id="byf0x9"
class Base:
registry = {}
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
Base.registry[cls.__name__] = cls
```Nên sử dụng siêu dữ liệu khi chúng đơn giản hóa hệ thống chứ không phải khi chúng chỉ mang lại cảm giác mạnh mẽ.
## 44.31 Ví dụ về siêu dữ liệu: Không gian tên tùy chỉnh
Siêu dữ liệu có thể từ chối các tên thuộc tính trùng lặp bằng cách trả về một không gian tên tùy chỉnh từ`__prepare__`.
```python id="9r9h1q"
class NoDuplicateDict(dict):
def __setitem__(self, key, value):
if key in self:
raise TypeError(f"duplicate name: {key}")
super().__setitem__(key, value)
class NoDuplicateMeta(type):
@classmethod
def __prepare__(mcls, name, bases):
return NoDuplicateDict()
class Example(metaclass=NoDuplicateMeta):
x = 1
y = 2
```Nếu như`x`được chỉ định hai lần, việc tạo lớp sẽ thất bại.
Đây là một trường hợp siêu dữ liệu phù hợp vì chỉ siêu dữ liệu mới có thể tùy chỉnh không gian tên nội dung lớp trước khi thực thi.
## 44.32 Các lớp tích hợp và các loại vùng heap
CPython có cả loại tích hợp được xác định tĩnh và loại vùng heap được phân bổ động.
Ví dụ về các loại tích hợp:```python id="bsov46"
int
str
list
dict
tuple
type
object
```Nhiều loại tích hợp được định nghĩa trong C.
Các lớp do người dùng định nghĩa là các kiểu heap được tạo trong thời gian chạy.
Cả hai đều tham gia vào cùng một mô hình đối tượng:```python id="fbhr47"
print(type(list))
print(type(object))
print(type(type))
```Đầu ra:```text id="errhki"
<class 'type'>
<class 'type'>
<class 'type'>
```Ngay cả các lớp dựng sẵn cũng là đối tượng.
## 44,33`object`Và`type`Mối quan hệ giữa`object`Và`type`là trung tâm.```python id="ls93k5"
print(isinstance(object, type))
print(isinstance(type, object))
print(type(type))
print(type(object))
```Sự thật quan trọng:```text id="9186px"
object is the base class of most Python objects
type is the default metaclass of classes
type is an instance of itself
object is an instance of type
type inherits from object
```Cấu trúc hình tròn này được khởi động cẩn thận bên trong CPython.
Nó cho phép các lớp trở thành đối tượng trong khi vẫn có hệ thống phân cấp kế thừa gốc.
## 44.34 Đối tượng loại CPython
Ở cấp độ C, một đối tượng lớp được biểu diễn bằng một đối tượng kiểu, thông thường`PyTypeObject`.
Một đối tượng loại chứa:```text id="pj1kt2"
object header
type name
basic instance size
item size for variable-sized objects
method table
slot functions
base classes
MRO
dictionary
flags
allocation functions
deallocation functions
attribute access functions
call behavior
numeric, sequence, and mapping operation tables
```Đây là lý do tại sao các lớp kiểm soát hành vi của đối tượng. Đối tượng lớp chứa các con trỏ hàm và siêu dữ liệu được sử dụng trong thời gian chạy.
## 44,35 Loại khe cắm
Các phương thức đặc biệt của Python thường tương ứng với các khe cấp độ C.
Ví dụ:
| Phương pháp Python | Hoạt động thời gian chạy |
|---|---|
|`__len__`| chiều dài hoạt động |
|`__getitem__`| lập chỉ mục |
|`__setitem__`| phân công mục |
|`__iter__`| lặp đi lặp lại |
|`__next__`| trình vòng lặp tiếp theo |
|`__call__`| gọi |
|`__add__`| phép cộng số |
|`__getattribute__`| tra cứu thuộc tính |
|`__new__`| phân bổ |
|`__init__`| khởi tạo |
Khi một lớp được tạo, CPython sẽ xây dựng các bảng vị trí từ các phương thức đặc biệt.
Điều này cho phép mã byte và API C gọi các hoạt động một cách hiệu quả mà không cần thực hiện tra cứu thuộc tính Python thông thường mỗi lần.
## 44.36 Tra cứu phương pháp đặc biệt
Tra cứu phương pháp đặc biệt thường bỏ qua các từ điển mẫu.
Ví dụ:```python id="w8gu52"
class Example:
pass
e = Example()
e.__len__ = lambda: 10
len(e)
```Điều này làm tăng`TypeError`, bởi vì`len(e)`tìm kiếm`__len__`trên loại, không phải là thuộc tính thể hiện tùy ý.
Chính xác:```python id="a41p2c"
class Example:
def __len__(self):
return 10
e = Example()
print(len(e))
```Các phương thức đặc biệt thuộc về lớp để CPython có thể điền và sử dụng các vị trí kiểu.
## 44.37 Tra cứu thuộc tính cho các lớp
Để truy cập ví dụ:```python id="ars6ez"
obj.attr
```CPython tìm kiếm phiên bản và hệ thống phân cấp lớp của nó.
Để truy cập lớp:```python id="e6vx8d"
Class.attr
```CPython tìm kiếm đối tượng lớp và siêu dữ liệu của nó.
Ví dụ:```python id="511n1a"
class Meta(type):
label = "meta"
class User(metaclass=Meta):
label = "class"
print(User.label)
```Đầu ra:```text id="8dznft"
class
```Nếu như`label`vắng mặt ở`User`, tra cứu có thể tìm thấy nó trên`Meta`.
Siêu lớp xác định hành vi và thuộc tính của các đối tượng lớp.
## 44.38 Phương thức siêu lớp
Phương thức siêu dữ liệu là một phương thức trên đối tượng lớp.```python id="7avylj"
class Meta(type):
def describe(cls):
return cls.__name__
class User(metaclass=Meta):
pass
print(User.describe())
```Đây,`describe`được tìm thấy trên`Meta`và ràng buộc với`User`.
Điều này tương tự với cách tìm thấy các phương thức instance trên`User`và ràng buộc với`user_instance`.
```text id="dm9zxe"
instance method:
User.method -> bound to instance
metaclass method:
Meta.method -> bound to class object
```## 44.39 Các lớp và bộ mô tả
Mô tả lưu trữ từ điển lớp.```python id="jcw8ig"
class User:
@property
def name(self):
return "Ada"
print(User.__dict__["name"])
```Khi một instance truy cập`name`, logic mô tả sẽ chạy.```python id="bochjq"
u = User()
print(u.name)
```Các phương thức cũng là mô tả:```python id="yrosvm"
class User:
def hello(self):
return "hello"
u = User()
print(u.hello)
```Chức năng được lưu trữ trong`User.__dict__`liên kết với`u`thông qua mô tả của nó`__get__`.
## 44.40 Lớp học và thu gom rác
Các đối tượng lớp có thể tham gia vào các chu trình tham chiếu.
Ví dụ:```text id="zbbqva"
class object references methods
methods reference globals
functions reference code objects
closures may reference class-related state
instances reference class
class may reference descriptors
descriptors may reference owner class
```Trình thu gom rác theo chu kỳ của CPython có thể thu thập các lớp không thể truy cập và các đối tượng liên quan nếu chúng không được các tham chiếu khác duy trì.
Hầu hết các lớp thông thường tồn tại cho đến khi quá trình thoát ra vì các mô-đun giữ các tham chiếu đến chúng.```python id="25zzxi"
class User:
pass
```Từ điển mô-đun chứa`User`.
## 44.41 Lớp và Mô-đun
Một lớp ghi lại mô-đun nơi nó được xác định.```python id="23z7po"
class User:
pass
print(User.__module__)
```Thường xuyên:```text id="btn8n6"
__main__
```hoặc tên mô-đun.
Điều này ảnh hưởng đến việc trình bày, tẩy rửa, ghi chép và xem xét nội tâm.
Một lớp không thuộc về một mô-đun về mặt vật lý. Từ điển mô-đun chỉ chứa một tham chiếu đến đối tượng lớp.```python id="vxme1s"
OtherName = User
```Bây giờ đối tượng lớp đó có một tên khác.
## 44.42 Nhận dạng lớp
Nhận dạng lớp là nhận dạng đối tượng.```python id="05l12d"
class User:
pass
A = User
B = User
print(A is B)
```Đầu ra:```text id="krnpze"
True
```Nếu một mô-đun được nhập hai lần dưới các tên khác nhau, các đối tượng lớp có thể bị trùng lặp.```text id="59akdg"
package.models.User
models.User
```Đây có thể là các đối tượng lớp khác nhau ngay cả khi chúng đến từ cùng một tệp.
Điều đó phá vỡ`isinstance`, tra cứu sổ đăng ký, tuần tự hóa và các giả định đơn lẻ.
## 44.43 Chú thích lớp
Chú thích lớp được lưu trữ trong`__annotations__`.
```python id="qmsv40"
class User:
id: int
name: str = "anonymous"
print(User.__annotations__)
```Đầu ra:```text id="fzxf79"
{'id': <class 'int'>, 'name': <class 'str'>}
```Chú thích không tự động tạo các trường mẫu.```python id="pveplo"
u = User()
print(hasattr(u, "id"))
```Thường xuyên:```text id="lxeohq"
False
```Các khung như lớp dữ liệu, attrs, Pydantic và ORM kiểm tra các chú thích để tạo ra hành vi.
## 44.44 Các lớp dữ liệu dưới dạng chuyển đổi lớp`dataclasses.dataclass`là người trang trí lớp học.```python id="r4xqnl"
from dataclasses import dataclass
@dataclass
class User:
id: int
name: str
```Nó nhận đối tượng lớp, kiểm tra các chú thích và thêm các phương thức như:```text id="i9m60v"
__init__
__repr__
__eq__
```Điều này cho thấy hành vi chính của lớp có thể được thêm vào sau khi tạo lớp mà không cần siêu dữ liệu.
## 44.45 Các lớp cơ sở trừu tượng
các`abc`mô-đun sử dụng máy móc siêu dữ liệu.```python id="cudwvv"
from abc import ABC, abstractmethod
class Store(ABC):
@abstractmethod
def get(self, key):
pass
ABCcông dụngABCMeta, một siêu dữ liệu theo dõi các phương thức trừu tượng và ngăn chặn việc khởi tạo cho đến khi chúng được triển khai.```python id="j5b315"
class BadStore(Store):
pass
BadStore()
```Điều này làm tăngTypeError.
Các lớp cơ sở trừu tượng là một ví dụ thực tế về siêu lớp thực thi các hợp đồng lớp.
44.46 Lớp học vàisinstance
isinstance(obj, cls)thường kiểm tra xemtype(obj)làclshoặc một lớp con củacls.
class Animal:
pass
class Dog(Animal):
pass
d = Dog()
print(isinstance(d, Dog))
print(isinstance(d, Animal))
```Siêu dữ liệu có thể tùy chỉnh điều này thông qua`__instancecheck__`.
Máy ABC sử dụng điều này cho hành vi của lớp con ảo.
## 44.47 Lớp học và`issubclass`
`issubclass(A, B)`kiểm tra xem lớp`A`là một lớp con của lớp`B`.
```python id="3tkb0g"
class A:
pass
class B(A):
pass
print(issubclass(B, A))
```Siêu dữ liệu có thể tùy chỉnh điều này thông qua`__subclasscheck__`.
Đây là một nơi khác mà hành vi của lớp được trung gian bởi logic siêu lớp.
## 44.48 Tạo lớp động
Tạo lớp động rất hữu ích trong các khung và tạo mã.```python id="pq2ye4"
def make_model(name, fields):
namespace = {"__annotations__": fields}
return type(name, (), namespace)
User = make_model("User", {"id": int, "name": str})
print(User)
print(User.__annotations__)
```Các lớp động vẫn phải tuân theo các quy tắc lớp thông thường:```text id="kwqh7u"
valid name
clear module
stable identity
predictable bases
explicit public API
```Bộ`__module__`khi cần thiết:```python id="hyvbmw"
namespace = {
"__module__": __name__,
"__annotations__": fields,
}
```Điều này cải thiện sự xem xét nội tâm và tuần tự hóa.
## 44.49 Khi nào nên sử dụng Metaclass
Sử dụng siêu dữ liệu khi bạn cần một trong những thứ sau:```text id="j2iimq"
custom class namespace through __prepare__
control over class allocation through __new__
metaclass-level methods or properties
custom class call behavior
deep integration across a class hierarchy
framework-level class validation
special instance or subclass checks
```Tránh sử dụng siêu dữ liệu để đăng ký, xác thực hoặc tạo phương thức đơn giản khi trình trang trí lớp hoặc`__init_subclass__`là đủ.
Siêu dữ liệu có tính toàn cầu đối với hệ thống phân cấp lớp. Chúng soạn thảo kém khi các thư viện không liên quan xác định các siêu dữ liệu khác nhau.
## 44,50 Điểm chính
Một lớp là một đối tượng thời gian chạy.
Hầu hết các lớp là những thể hiện của`type`.
Siêu dữ liệu là loại của một lớp.
Các định nghĩa lớp thực thi phần thân, thu thập một vùng tên và gọi một siêu dữ liệu để tạo đối tượng lớp.
Các phiên bản được tạo bằng cách gọi một lớp, được xử lý bởi siêu dữ liệu`__call__`.
Từ điển lớp lưu trữ các phương thức, bộ mô tả, chú thích và biến lớp.
Kế thừa sử dụng thứ tự phân giải phương thức.
Các phương thức đặc biệt được cài đặt vào các khe kiểu và thường được tra cứu trên lớp chứ không phải trên phiên bản.
mô tả,`__set_name__`, `__init_subclass__`, trình trang trí lớp và siêu lớp đều tham gia xây dựng lớp.
Chỉ sử dụng siêu dữ liệu khi các cơ chế lớp đơn giản hơn không thể thể hiện hành vi được yêu cầu.