Python __new__ ví dụ

Nếu bạn là người mới sử dụng Python hoặc một nhà phát triển dày dạn kinh nghiệm thì rất có thể bạn đã thấy phương thức __init__ đặc biệt. Phát âm là “Dunder” hoặc gạch dưới kép là một phương thức đặc biệt khi khởi tạo một lớp. Trong các ngôn ngữ khác hỗ trợ lập trình hướng đối tượng, nó được gọi là hàm tạo. Phương thức __init__ được gọi khi một đối tượng được tạo từ lớp và cho phép nó khởi tạo các thuộc tính của lớp. Khá dễ dàng phải không?

Trong bài viết này, tôi muốn thảo luận về một trong những tính năng bị hiểu lầm nhiều nhất của Python là phương thức __new__. Chúng ta sẽ bắt đầu bằng cách đơn giản nói rằng cả hai phương thức __init__ và __new__ đều được gọi khi cố gắng tạo một thể hiện của một lớp. Theo tài liệu chính thức của Python, __new__ được sử dụng khi bạn cần kiểm soát việc tạo phiên bản mới trong khi __init__ được sử dụng để kiểm soát việc khởi tạo phiên bản mới. Về cơ bản, khi tạo các đối tượng Python, __new__ là phương thức đầu tiên được gọi và chịu trách nhiệm trả về một thể hiện mới của lớp

Mặt khác, __init__ không trả lại bất cứ thứ gì. Nó chỉ chịu trách nhiệm khởi tạo thể hiện SAU KHI nó được tạo. Một nguyên tắc nhỏ đơn giản là lớp __new__ không bao giờ được ghi đè trừ khi bạn đang phân lớp con một kiểu dữ liệu Python bất biến, chẳng hạn như kiểu dữ liệu số, chuỗi, byte, bộ cố định và bộ dữ liệu

Tại thời điểm này, bạn có thể nghĩ “Thật tuyệt, tôi cũng có thể đọc tài liệu. Khi nào tôi thực sự cần sử dụng cái này?” . Chúng ta có thể bắt đầu bằng cách tạo một đối tượng proxy viết thường tất cả các đối số và sau đó chuyển tiếp tất cả các lời gọi phương thức của nó đến bộ bên dưới. Tuy nhiên, làm điều này sẽ làm giảm hiệu suất đáng kể vì nó được viết bằng Python. Trong khi loại bộ tiêu chuẩn được viết bằng C làm cho nó nhanh hơn đáng kể so với bất kỳ mã Python nào mà chúng ta có thể viết. Một giả định ngây thơ nhưng công bằng khác là bắt đầu hạ thấp các bộ dữ liệu trong phương thức __init__

Tuy nhiên, lúc này đối tượng đã tồn tại trong bộ nhớ và không thể thay đổi [immutable]. Cách duy nhất để giải quyết vấn đề này là chặn và sửa đổi đối số trước khi đối tượng được tạo. Đây là nơi __new__ phát huy tác dụng

class LowerCaseTuple[tuple]:
def __new__[cls, iterable]:
lower_iterable = [l.lower[] for l in iterable]
return super[].__new[cls, lower_iterable]
def lower_case_example[]:
print[LowerCaseTuple[['HELLO', 'MEDIUM']]]
def main[]:
lower_case_example[]
if __name__ == '__main__':
main[]

Chạy chương trình sau sẽ dẫn đến một bộ phần tử chữ thường

['hello', 'medium']

Lý do điều này hiện có thể thực hiện được là do phương thức __new__ luôn chạy trước __init__ do đây là phương thức tĩnh cấp lớp. Khi một thể hiện của một lớp được tạo bằng cách gọi hàm tạo của lớp, phương thức __new__ sẽ được gọi trước để tạo thể hiện của lớp, sau đó thể hiện sẽ là phương thức __init__ cấp thể hiện để khởi tạo thể hiện của lớp. Điều quan trọng cần lưu ý là nếu __new__ không trả về một thể hiện của lớp mà nó bị ràng buộc [e. g. LowerCaseTuple], nó hoàn toàn bỏ qua bước xây dựng và sẽ trả về loại Không có. Để thấy điều này hoạt động, chỉ cần nhận xét câu lệnh trả về trong phương thức __new__ và chạy tập lệnh trên

Nếu bạn thích bài viết này, xin vui lòng vỗ tay và nếu bạn có bất kỳ nhận xét nào, vui lòng để lại chúng. Ngoài ra, bạn có thể theo dõi tôi trên Medium để cập nhật các bài đăng mới nhất của tôi

Gần đây tôi đã hỏi tôi phương thức

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
1 dunder đã làm gì trong Python. Và tôi đã nói, "nó được sử dụng để khởi tạo các biến bên trong một lớp". Và ngay sau đó, câu hỏi tiếp theo là, “vậy thì
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
2 dùng để làm gì?”. Và tôi hoàn toàn trống rỗng và không thể trả lời rằng

Tôi không thể trả lời câu hỏi đó vì không có nhiều hướng dẫn nói về

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
2. Tôi không muốn xảy ra điều này với bạn. Và đó là lý do tại sao tôi nghĩ ra bài viết blog này cho bạn

Điểm tương đồng

Hãy bắt đầu với những điểm tương đồng

  • Cả hai đều được gọi/gọi trong quá trình tạo thể hiện

sự khác biệt

Hãy bắt đầu với sự khác biệt

newinit1Được gọi trước initĐược gọi sau new2Chấp nhận một loại làm đối số đầu tiênChấp nhận một thể hiện làm đối số đầu tiên3Được cho là trả về một thể hiện của loại đã nhậnKhông được phép trả về bất kỳ thứ gì4Được sử dụng để kiểm soát việc tạo cá thểĐược sử dụng để khởi tạo các biến thể hiện

Nói về điểm đầu tiên.

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
2 được gọi khi phiên bản được tạo lần đầu tiên. Điều này xảy ra trước khi khởi tạo lớp

Nhân tiện, bạn có lưu ý rằng đối số đầu tiên của

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
1 luôn là
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
6 không? .
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
6 là những gì
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
2 trả về

Đến điểm thứ ba,

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
2 được cho là trả về một thể hiện của lớp. Lưu ý rằng nếu
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
2 không trả về bất cứ thứ gì, thì
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
1 sẽ không được gọi

Mà một trong số họ là một nhà xây dựng?

Nếu bạn đến từ một ngôn ngữ khác, bạn có thể ngạc nhiên rằng có hai điều giống nhau đang làm cùng một loại công việc. Hầu hết các ngôn ngữ mà bạn có thể đã từng làm việc sẽ có một thứ gọi là hàm tạo

Trong Python, khái niệm đó được chia thành hàm tạo và trình khởi tạo. Và bạn có thể đoán ra,

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
2 là hàm tạo và
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
1 là hàm khởi tạo

Xin lưu ý rằng

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
2 là ẩn. Có nghĩa là nếu bạn không thực sự cần sửa đổi việc tạo một thể hiện của lớp, thì bạn không cần phải có phương thức
['hello', 'medium']
65

Một điều nữa tôi muốn thêm vào là… các biến thể hiện là cục bộ của một thể hiện. Vì vậy, bất cứ điều gì bạn đang làm trong init chỉ là cục bộ của phiên bản đó. Nhưng bất cứ điều gì bạn đang làm mới sẽ ảnh hưởng đến mọi thứ được tạo cho loại đó

Luồng thực thi với một ví dụ

Tôi sẽ thêm một số mã để làm cho điều này hấp dẫn hơn. Hãy xem xét ví dụ này

['hello', 'medium']
7
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]

Đây là ví dụ đơn giản nhất về cả

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
2 và
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
1 đang hoạt động. Nếu bạn lưu đoạn mã trên vào một tệp và chạy nó, bạn sẽ thấy như thế này

['hello', 'medium']
6

Như bạn có thể thấy, phương thức mới được gọi đầu tiên và sau đó việc thực thi được chuyển sang phương thức init

Trường hợp sử dụng

Trường hợp sử dụng cho
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
2

Một trong những trường hợp sử dụng tốt nhất mà tôi có thể lấy làm ví dụ là khi tạo một Singleton. Như chúng ta đã biết, Singleton đảm bảo một lớp chỉ có một thể hiện và cung cấp một điểm truy cập toàn cục cho nó

Một số nơi tôi đã thấy singleton đang hoạt động là trong lập trình trò chơi nơi chỉ có một phiên bản của trình phát. Một nơi khác, nếu bạn đã sử dụng các thư viện frontend như Vuex [hoặc Redux] thì chỉ có một phiên bản toàn cầu của cửa hàng. Không quan trọng bạn tạo bao nhiêu phiên bản, cuối cùng bạn sẽ chỉ có một

Hãy xem cách đạt được hành vi tương tự trong Python

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
3____24

đầu ra

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
5

Như bạn có thể thấy, việc tạo… chỉ được in một lần. cả hai đều trỏ đến cùng một vị trí bộ nhớ. Trong trường hợp của tôi, đó là

['hello', 'medium']
69

Bạn có thể đoán rằng chúng ta không thể làm điều tương tự với

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
1? . Đó là bởi vì
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
1 không trả lại bất cứ thứ gì. Chúng ta sẽ xem trong phần tiếp theo
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
1 phù hợp với điều gì

Nhưng trước tiên, tôi muốn cho bạn thấy một trường hợp sử dụng khác của new

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
0
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
1

đầu ra

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
2

Tôi không phải là nhà động vật học. Nhưng bạn hiểu ý tôi ở đây. Bạn có thể sử dụng

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
2 để tạo một thể hiện có điều kiện từ một lớp

Trường hợp sử dụng cho
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
1

Như chúng ta đã thấy trước đây.

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
35 ở đó để khởi tạo một biến thể hiện. Các biến đối tượng này sau này có thể được sử dụng trong các phương thức khác nhau của đối tượng

Tôi đã sử dụng rộng rãi

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
1 khi tôi từng làm việc với Qt framework. Qt là một khuôn khổ để phát triển giao diện người dùng dựa trên máy tính để bàn. Khi khởi tạo các đối tượng giao diện người dùng, bạn có thể đặt độ rộng hoặc độ dài của cửa sổ. Bạn cũng có thể đọc các tùy chọn từ một tệp và áp dụng tùy chọn đó trong giai đoạn khởi tạo ứng dụng. Đặt tiêu đề cửa sổ có thể là một ví dụ khác

Ở đây tôi sẽ chứng minh một ví dụ như vậy

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
7____20

Ví dụ trên không phải là một ví dụ hoàn chỉnh, nhưng khi được thiết lập chính xác, nó sẽ hiển thị một cửa sổ tương tự như thế này

Cửa sổ PySide mẫu

Chìa khóa rút ra

  • Trong hầu hết các trường hợp, bạn không cần
  • class Demo:
        def __new__[cls, *args]:
            print["__new__ called"]
            return object.__new__[cls]
    
        def __init__[self]:
            print["__init__ called"]
    
    d = Demo[]
    
    2 được gọi trước
    class Demo:
        def __new__[cls, *args]:
            print["__new__ called"]
            return object.__new__[cls]
    
        def __init__[self]:
            print["__init__ called"]
    
    d = Demo[]
    
    1
  • class Demo:
        def __new__[cls, *args]:
            print["__new__ called"]
            return object.__new__[cls]
    
        def __init__[self]:
            print["__init__ called"]
    
    d = Demo[]
    
    2 trả về một thể hiện của lớp
  • class Demo:
        def __new__[cls, *args]:
            print["__new__ called"]
            return object.__new__[cls]
    
        def __init__[self]:
            print["__init__ called"]
    
    d = Demo[]
    
    1 nhận các thể hiện của lớp được trả về bởi
    class Demo:
        def __new__[cls, *args]:
            print["__new__ called"]
            return object.__new__[cls]
    
        def __init__[self]:
            print["__init__ called"]
    
    d = Demo[]
    
    2
  • Sử dụng ________ 21 để khởi tạo giá trị

Khái niệm tương tự có thể được sử dụng để trả lời câu hỏi trừu tượng hóa và đóng gói

Sự kết luận

Đó là tất cả những gì tôi biết về

class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
1 so với
class Demo:
    def __new__[cls, *args]:
        print["__new__ called"]
        return object.__new__[cls]

    def __init__[self]:
        print["__init__ called"]

d = Demo[]
2. Nếu bạn có một cái gì đó trong tâm trí của bạn mà tôi bỏ lỡ. Làm ơn cho tôi biết

Tôi cũng sẽ liệt kê một số tài liệu tham khảo mà tôi muốn viết bài đăng trên blog này để bạn có thể thực sự là nguồn thông tin thực sự.

__ mới __ trong Python là gì?

Python __new__[] là phương thức khởi tạo kiểm soát việc tạo phiên bản mới . Nó được gọi đầu tiên và nó trả về một thể hiện của lớp mới. Python __init__[] là phương thức khởi tạo để thiết lập các thuộc tính [i. e. , trạng thái] của phiên bản mới được tạo. Nó được gọi sau khi tạo và không trả về gì cả, tôi. e. , Không có.

Sự khác biệt giữa __ khởi tạo __ và __ mới __ là gì?

__new__ là bước đầu tiên của quá trình tạo cá thể. Nó được gọi đầu tiên và chịu trách nhiệm trả về một phiên bản mới của lớp của bạn. Ngược lại, __init__ không trả lại bất cứ thứ gì;

Có phải __ hàm tạo __ mới không?

Và bạn có thể đoán, __new__ là hàm tạo và __init__ là hàm khởi tạo. Xin lưu ý rằng __new__ là ẩn. Có nghĩa là nếu bạn không thực sự cần sửa đổi việc tạo một thể hiện của lớp, thì bạn không cần phải có một phương thức mới.

Có cái gì bên trong __?

Phương thức __init__ là tương đương Python với hàm tạo C++ theo cách tiếp cận hướng đối tượng . Hàm __init__ được gọi mỗi khi một đối tượng được tạo từ một lớp. Phương thức __init__ cho phép lớp khởi tạo các thuộc tính của đối tượng và không phục vụ mục đích nào khác. Nó chỉ được sử dụng trong các lớp học.

Chủ Đề