Từ điển xây dựng lớp Python

Tham số hàm tạo lớp. từ điển

class Bunch(object):
    def __init__(self, **fields): 
        self.__dict__ = fields
        
p = Bunch(x=2.3, y=4.5)
print p                     

print p.__dict__
           
       

Ví dụ liên quan trong cùng thể loại

Bản chất Python là một ngôn ngữ rất năng động. Các biến không cần khai báo và có thể được thêm dưới dạng thuộc tính ở hầu hết mọi nơi. Hãy xem xét một lớp trống như thế này

Đây là một định nghĩa đầy đủ về một lớp trong Python. Cấp, nó không làm gì cả, nhưng nó vẫn hợp lệ

Tại bất kỳ thời điểm nào sau này, chúng ta có thể "vá" các thuộc tính cho lớp của mình như thế này

MyClass.class_attribute = 42

Lớp có giá trị

>>> my_object.class_attribute
42
5 mới này kể từ thời điểm đó

Nếu chúng tôi khởi tạo lớp này với

>>> my_object.class_attribute
42
6, chúng tôi có thể xác minh rằng giá trị
>>> my_object.class_attribute
42
5 là 42

>>> my_object.class_attribute
42

Tất nhiên, chúng ta cũng có thể thêm các thuộc tính vào các thể hiện của mình

>>> my_object.instance_attribute = 21
>>> my_object.instance_attribute
21

Bạn đã bao giờ tự hỏi những thuộc tính này được lưu trữ ở đâu chưa?

Rõ ràng là tốt hơn ngầm. (từ Thiền của Python)

Python sẽ không phải là Python nếu không có hành vi được xác định rõ ràng và có thể tùy chỉnh cho các thuộc tính. Các thuộc tính của một “thứ” trong Python được lưu trữ trong một thuộc tính ma thuật có tên là

>>> my_object.class_attribute
42
8. Chúng ta có thể truy cập nó như vậy

________số 8

Như bạn có thể thấy,

>>> my_object.class_attribute
42
5 được lưu trữ trong chính
>>> my_object.class_attribute
42
8 của
>>> my_object.instance_attribute = 21
>>> my_object.instance_attribute
21
1, trong khi
>>> my_object.instance_attribute = 21
>>> my_object.instance_attribute
21
2 được lưu trữ trong
>>> my_object.class_attribute
42
8 của
>>> my_object.instance_attribute = 21
>>> my_object.instance_attribute
21
4

Điều đó có nghĩa là, bất cứ khi nào bạn truy cập vào

>>> my_object.instance_attribute = 21
>>> my_object.instance_attribute
21
5, Python sẽ tìm trong
>>> my_object.instance_attribute = 21
>>> my_object.instance_attribute
21
6 trước, sau đó là trong
>>> my_object.instance_attribute = 21
>>> my_object.instance_attribute
21
7. Nếu thuộc tính
>>> my_object.instance_attribute = 21
>>> my_object.instance_attribute
21
2 không được tìm thấy trong từ điển, thì một
>>> my_object.instance_attribute = 21
>>> my_object.instance_attribute
21
9 sẽ xuất hiện

Ghi chú bên lề

Một "thứ" trong Python là gì? . Về mặt logic, một lớp như

>>> my_object.instance_attribute = 21
>>> my_object.instance_attribute
21
1 thuộc loại
class MyClass:
    class_attribute = "Class"

    def __init__(self):
        self.instance_attribute = "Instance"

my_object = MyClass()

print(my_object.__dict__)
print(MyClass.__dict__)
2, nghĩa là bản thân lớp đó là một đối tượng thuộc loại
class MyClass:
    class_attribute = "Class"

    def __init__(self):
        self.instance_attribute = "Instance"

my_object = MyClass()

print(my_object.__dict__)
print(MyClass.__dict__)
2. Vì điều này nghe có vẻ khó hiểu nên tôi sử dụng thuật ngữ thông tục “thứ” để thay thế

“Hack” thuộc tính >>> my_object.class_attribute 428

Như mọi khi trong Python, thuộc tính

>>> my_object.class_attribute
42
8 hoạt động giống như bất kỳ thuộc tính nào khác trong Python. Vì Python là ngôn ngữ thích chuyển qua tham chiếu, nên chúng ta có thể xem xét một lỗi xảy ra khá thường xuyên do tình cờ. Hãy xem xét một lớp
class MyClass:
    class_attribute = "Class"

    def __init__(self):
        self.instance_attribute = "Instance"

my_object = MyClass()

print(my_object.__dict__)
print(MyClass.__dict__)
6

>>> my_object.class_attribute
42
7

Bây giờ, hãy tạo một số sổ địa chỉ và tạo một số địa chỉ

>>> my_object.class_attribute
42
8

Thật thú vị, Alice và Bob hiện chia sẻ một sổ địa chỉ

>>> my_object.class_attribute
42
9

Điều này là do thuộc tính

class MyClass:
    class_attribute = "Class"

    def __init__(self):
        self.instance_attribute = "Instance"

my_object = MyClass()

print(my_object.__dict__)
print(MyClass.__dict__)
7 được xác định ở cấp độ
class MyClass:
    class_attribute = "Class"

    def __init__(self):
        self.instance_attribute = "Instance"

my_object = MyClass()

print(my_object.__dict__)
print(MyClass.__dict__)
2. Danh sách trống chỉ được tạo một lần (
class MyClass:
    class_attribute = "Class"

    def __init__(self):
        self.instance_attribute = "Instance"

my_object = MyClass()

print(my_object.__dict__)
print(MyClass.__dict__)
9), cụ thể là khi trình thông dịch Python tạo lớp. Do đó, đối với bất kỳ trường hợp tiếp theo nào của lớp
class MyClass:
    class_attribute = "Class"

    def __init__(self):
        self.instance_attribute = "Instance"

my_object = MyClass()

print(my_object.__dict__)
print(MyClass.__dict__)
6, cùng một
>>> my_object.class_attribute
42
71 được tham chiếu bởi
class MyClass:
    class_attribute = "Class"

    def __init__(self):
        self.instance_attribute = "Instance"

my_object = MyClass()

print(my_object.__dict__)
print(MyClass.__dict__)
7. Chúng ta có thể sửa lỗi này bằng cách di chuyển việc tạo
>>> my_object.class_attribute
42
71 trống sang cấp độ phiên bản như vậy

>>> my_object.class_attribute
42
7

Bằng cách di chuyển việc tạo danh sách trống sang hàm tạo (phương thức ______274), một danh sách mới được tạo bất cứ khi nào một phiên bản mới của

class MyClass:
    class_attribute = "Class"

    def __init__(self):
        self.instance_attribute = "Instance"

my_object = MyClass()

print(my_object.__dict__)
print(MyClass.__dict__)
6 được tạo. Do đó, các phiên bản không vô tình chia sẻ cùng một
>>> my_object.class_attribute
42
71 nữa

Giới thiệu Borg

Chúng ta có thể tận dụng hành vi này bằng cách nào đó có chủ ý không? . Có một Mẫu thiết kế được gọi là Singleton. Điều này đảm bảo rằng chỉ có một thể hiện của lớp trong thời gian chạy chương trình. Ví dụ: nó có thể hữu ích nếu điều này được sử dụng cho lớp kết nối cơ sở dữ liệu hoặc kho lưu trữ cấu hình

Lưu ý rằng bạn chỉ nên thỉnh thoảng sử dụng các lớp đơn lẻ vì chúng giới thiệu một số trạng thái chung trong chương trình của bạn, điều này gây khó khăn cho việc kiểm tra các thành phần riêng lẻ trong chương trình của bạn một cách cô lập

Điều gì sẽ là một cách Pythonic để thực hiện một mẫu đơn lẻ?

Hãy xem xét lớp học này

>>> my_object.class_attribute
42
1

Lớp này có thuộc tính

>>> my_object.class_attribute
42
77 được khởi tạo dưới dạng một mảng trống. Chúng ta đã biết từ các đoạn trước rằng đối tượng
>>> my_object.class_attribute
42
78 là cùng một đối tượng cho lớp. Sau đó, bên trong hàm tạo (
>>> my_object.class_attribute
42
74), chúng tôi đặt
>>> my_object.class_attribute
42
8 của thể hiện thành từ điển dùng chung này. Do đó, tất cả các thuộc tính được thêm động được chia sẻ giữa mỗi phiên bản của lớp đó

Hãy kiểm tra

>>> my_object.class_attribute
42
6

Tại sao chúng ta không thể đặt trực tiếp

>>> my_object.class_attribute
42
81 cho lớp như vậy

>>> my_object.class_attribute
42
0

Điều này là do trong trường hợp sau, chúng tôi đặt thuộc tính

>>> my_object.class_attribute
42
8 cho chính lớp đó. Tuy nhiên, chúng tôi truy cập thuộc tính của thể hiện bằng cách gõ
>>> my_object.class_attribute
42
83. Chỉ khi thuộc tính
>>> my_object.class_attribute
42
8 được đặt ở cấp độ phiên bản, chúng tôi mới có thể sử dụng mẫu Borg của mình. Cách để đạt được điều này là sử dụng hàm tạo để thay đổi thuộc tính
>>> my_object.class_attribute
42
8 ở cấp độ thể hiện

Sử dụng bộ nhớ của các thuộc tính

Tự động thêm các thuộc tính trong thời gian chạy ở cấp độ phiên bản hoặc cấp độ lớp đi kèm với chi phí. Cấu trúc từ điển khá tốn bộ nhớ trong nội bộ của Python. Trong trường hợp bạn khởi tạo rất nhiều (hàng nghìn) phiên bản, điều này có thể trở thành nút cổ chai

Tuy nhiên, điều đầu tiên trước tiên. Khe cắm là gì? . Khi bạn thêm thuộc tính

>>> my_object.class_attribute
42
86 vào
class MyClass:
    class_attribute = "Class"

    def __init__(self):
        self.instance_attribute = "Instance"

my_object = MyClass()

print(my_object.__dict__)
print(MyClass.__dict__)
2, bạn xác định trước thuộc tính thành viên nào bạn cho phép. Chúng ta hãy có một cái nhìn

>>> my_object.class_attribute
42
1

Với định nghĩa này, bất kỳ phiên bản nào của

>>> my_object.class_attribute
42
88 chỉ có thể truy cập thuộc tính
>>> my_object.class_attribute
42
89. Truy cập các thuộc tính (động) khác sẽ làm tăng
>>> my_object.instance_attribute = 21
>>> my_object.instance_attribute
21
9

>>> my_object.class_attribute
42
2

Hạn chế khả năng thêm thuộc tính động rất hữu ích để giảm lỗi thời gian chạy có thể xảy ra do lỗi chính tả trong tên thuộc tính. Tuy nhiên, quan trọng hơn, hạn chế này sẽ làm giảm mức sử dụng bộ nhớ của mã của bạn – trong một số trường hợp đáng kể. Hãy thử kiểm tra điều này

Chúng tôi tạo hai lớp, một lớp có rãnh và một lớp không có rãnh. Cả hai lớp đều truy cập một thuộc tính có tên là

>>> my_object.class_attribute
42
89 bên trong phương thức
>>> my_object.class_attribute
42
74 của chúng và trong trường hợp của lớp có rãnh, đó là thuộc tính duy nhất trong
>>> my_object.class_attribute
42
86

Chúng tôi tạo một triệu phiên bản cho mỗi lớp và lưu trữ các phiên bản này trong danh sách. Sau đó, chúng tôi xem kích thước của danh sách. Danh sách các thể hiện của lớp có rãnh nên nhỏ hơn

>>> my_object.class_attribute
42
3

Tuy nhiên, chúng tôi nhận lại giá trị là

>>> my_object.class_attribute
42
94 cho mỗi danh sách. Vậy làm thế nào để chúng ta tiết kiệm bộ nhớ sau đó sử dụng các khe cắm?

Hãy sử dụng mô-đun sử dụng bộ nhớ ipython để kiểm tra lượng bộ nhớ được tiêu thụ trong thời gian chạy chương trình thử nghiệm của chúng tôi

>>> my_object.class_attribute
42
4

Như bạn có thể thấy, phiên bản có rãnh chỉ chiếm khoảng 85 MiB RAM, trong khi phiên bản không có rãnh cần hơn 200 MiB, mặc dù kích thước kết quả của các danh sách là như nhau

Lý do cho điều này là cách Python xử lý nội bộ của

>>> my_object.class_attribute
42
78. Khi không chỉ định
>>> my_object.class_attribute
42
86, Python mặc định sử dụng từ điển để lưu trữ các thuộc tính. Từ điển này có bản chất động, có thể thay đổi kích thước, cần được sắp xếp theo các phím, v.v. Đó là lý do tại sao Python cần rất nhiều bộ nhớ để quản lý từ điển

Trong phiên bản có rãnh của lớp, các tính năng chính của

>>> my_object.class_attribute
42
78 không còn cần thiết nữa vì không cho phép thay đổi kích thước động nữa. Do đó, Python phân bổ bộ nhớ trước cho các thuộc tính được đề cập trong
>>> my_object.class_attribute
42
86

Tôi nên sử dụng dict() hay {}?

tl;dr. Với CPython 2. 7, việc sử dụng dict() để tạo từ điển mất tới 6 lần lâu hơn và liên quan đến nhiều hoạt động cấp phát bộ nhớ hơn so với cú pháp theo nghĩa đen. Sử dụng {} để tạo từ điển, đặc biệt nếu bạn điền sẵn từ điển, trừ khi cú pháp theo nghĩa đen không phù hợp với trường hợp của bạn .

Bạn có thể đặt một từ điển trong một lớp Python không?

Nếu bạn muốn sử dụng từ điển trên toàn cầu trong một lớp, thì bạn cần xác định từ đó trong phần nơi bạn sử dụng lớp của mình . nếu bạn đang sử dụng lớp của mình trong chính, thì hãy xác định nó ở đó. Theo mặc định, một từ điển hoặc danh sách o là toàn cầu.

{} có phải là từ điển trong Python không?

Từ điển Python là gì? . Từ điển được viết bằng dấu ngoặc nhọn ({}), bao gồm các cặp khóa-giá trị được phân tách bằng dấu phẩy (,) . Một dấu hai chấm (. ) tách từng khóa khỏi giá trị của nó.

__ dict __ trong lớp Python là gì?

__dict__ là Từ điển hoặc đối tượng ánh xạ khác được sử dụng để lưu trữ các thuộc tính (có thể ghi) của đối tượng . Hay nói một cách đơn giản, mọi đối tượng trong python đều có một thuộc tính được ký hiệu là __dict__. Và đối tượng này chứa tất cả các thuộc tính được xác định cho đối tượng.