Được rồi, điều đầu tiên trước tiên.
Không có cái gọi là "khai báo biến" hoặc "khởi tạo biến" trong Python.
Đơn giản là chúng ta gọi là "chuyển nhượng", nhưng có lẽ chỉ nên gọi "đặt tên".
Phép gán có nghĩa là "tên này ở phía bên trái bây giờ đề cập đến kết quả đánh giá phía bên phải, bất kể nó đã đề cập đến những gì trước đây (nếu có)".
foo = 'bar' 
foo = 2 * 3 
Do đó, tên của Python (một thuật ngữ tốt hơn "biến", được cho là) không có các kiểu liên kết; các giá trị nào. Bạn có thể áp dụng lại cùng một tên cho bất kỳ thứ gì bất kể loại của nó là gì, nhưng thứ đó vẫn có hành vi phụ thuộc vào loại của nó. Tên chỉ đơn giản là một cách để tham chiếu đến giá trị (đối tượng). Điều này trả lời câu hỏi thứ hai của bạn: Bạn không tạo biến để giữ một loại tùy chỉnh. Bạn không tạo biến để giữ bất kỳ kiểu cụ thể nào. Bạn không "tạo" biến nào cả. Bạn đặt tên cho các đối tượng.
Điểm thứ hai: Python tuân theo một quy tắc rất đơn giản khi nói đến các lớp, điều đó thực sự nhất quán hơn nhiều so với những gì các ngôn ngữ như Java, C ++ và C # làm: mọi thứ được khai báo bên trong classkhối đều là một phần của lớp . Vì vậy, các hàm ( def) được viết ở đây là các phương thức, tức là một phần của đối tượng lớp (không được lưu trữ trên cơ sở từng trường hợp), giống như trong Java, C ++ và C #; nhưng các tên khác ở đây cũng là một phần của lớp. Một lần nữa, các tên chỉ là tên và chúng không có các kiểu liên kết và các hàm cũng là các đối tượng trong Python. Như vậy:
class Example:
    data = 42
    def method(self): pass
Các lớp cũng là các đối tượng , trong Python.
Vì vậy, bây giờ chúng ta đã tạo một đối tượng có tên Example, đại diện cho lớp của tất cả những thứ là Examples. Đối tượng này có hai thuộc tính do người dùng cung cấp (Trong C ++, "thành viên"; trong C #, "trường hoặc thuộc tính hoặc phương thức"; trong Java, "trường hoặc phương thức"). Một trong số chúng được đặt tên datavà nó lưu giá trị số nguyên 42. Người khác được đặt tênmethod và nó lưu trữ một đối tượng hàm. (Có một số thuộc tính khác mà Python tự động thêm vào.)
Tuy nhiên, những thuộc tính này vẫn không thực sự là một phần của đối tượng. Về cơ bản, một đối tượng chỉ là một tập hợp nhiều tên hơn (tên thuộc tính), cho đến khi bạn chuyển sang những thứ không thể chia ra được nữa. Do đó, các giá trị có thể được chia sẻ giữa các trường hợp khác nhau của một lớp, hoặc thậm chí giữa các đối tượng của các lớp khác nhau, nếu bạn cố tình thiết lập điều đó.
Hãy tạo một phiên bản:
x = Example()
Bây giờ chúng ta có một đối tượng riêng biệt được đặt tên x, là một ví dụ của Example. Các datavà methodthực sự không phải là một phần của đối tượng, nhưng chúng ta vẫn có thể tra cứu chúng xnhờ một số phép thuật mà Python thực hiện đằng sau hậu trường. methodĐặc biệt, khi chúng ta tra cứu , thay vào đó, chúng ta sẽ nhận được một "phương thức bị ràng buộc" (khi chúng ta gọi nó, nó xsẽ tự động được truyền dưới dạng selftham số, điều này không thể xảy ra nếu chúng ta tra cứu Example.methodtrực tiếp).
Điều gì xảy ra khi chúng ta cố gắng sử dụng x.data?
Khi chúng tôi kiểm tra nó, nó sẽ được nhìn lên đối tượng đầu tiên. Nếu nó không được tìm thấy trong đối tượng, Python sẽ tìm trong lớp.
Tuy nhiên, khi chúng ta gán cho x.data , Python sẽ tạo một thuộc tính trên đối tượng. Nó sẽ không thay thế thuộc tính class '.
Điều này cho phép chúng ta khởi tạo đối tượng . Python sẽ tự động gọi __init__phương thức của lớp trên các phiên bản mới khi chúng được tạo, nếu có. Trong phương pháp này, chúng ta có thể chỉ cần gán cho các thuộc tính để đặt các giá trị ban đầu cho thuộc tính đó trên mỗi đối tượng:
class Example:
    name = "Ignored"
    def __init__(self, name):
        self.name = name
    
Bây giờ chúng ta phải chỉ định a namekhi chúng ta tạo một Example, và mỗi thể hiện có riêng của nó name. Python sẽ bỏ qua thuộc tính lớp Example.namebất cứ khi nào chúng ta tìm kiếm thuộc tính .namecủa một cá thể, vì thuộc tính của cá thể đó sẽ được tìm thấy đầu tiên.
Một lưu ý cuối cùng: sửa đổi (đột biến) và chuyển nhượng là những thứ khác nhau!
Trong Python, các chuỗi là bất biến. Chúng không thể được sửa đổi. Khi bạn làm:
a = 'hi '
b = a
a += 'mom'
Bạn không thay đổi chuỗi 'hi' ban đầu. Điều đó là không thể trong Python. Thay vào đó, bạn tạo một chuỗi mới'hi mom' và khiến anó không còn là tên nữa 'hi ', và 'hi mom'thay vào đó bắt đầu là tên . Chúng tôi đã bđặt tên cho 'hi 'cũng như sau khi áp dụng lại atên, bvẫn là một tên cho 'hi ', vì 'hi 'vẫn tồn tại và không bị thay đổi.
Nhưng danh sách có thể được thay đổi:
a = [1, 2, 3]
b = a
a += [4]
Bây giờ blà [1, 2, 3, 4], bởi vì chúng tôi đã bđặt tên cho cùng một thứ đã ađặt tên, và sau đó chúng tôi đã thay đổi thứ đó. Chúng tôi đã không tạo một danh sách mới ađể đặt tên, vì Python chỉ đơn giản là xử lý +=khác nhau cho các danh sách.
Điều này quan trọng đối với các đối tượng vì nếu bạn có một danh sách làm thuộc tính lớp và sử dụng một thể hiện để sửa đổi danh sách, thì thay đổi sẽ được "nhìn thấy" trong tất cả các trường hợp khác. Điều này là do (a) dữ liệu thực sự là một phần của đối tượng lớp chứ không phải bất kỳ đối tượng cá thể nào; (b) bởi vì bạn đang sửa đổi danh sách và không thực hiện một phép gán đơn giản, bạn đã không tạo một thuộc tính thể hiện mới ẩn thuộc tính lớp.