Đượ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 class
khố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à Example
s. Đố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 data
và 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 data
và method
thự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 x
nhờ 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ó x
sẽ tự động được truyền dưới dạng self
tham số, điều này không thể xảy ra nếu chúng ta tra cứu Example.method
trự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 name
khi 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.name
bất cứ khi nào chúng ta tìm kiếm thuộc tính .name
củ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 a
nó 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 a
tên, b
vẫ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ờ b
là [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.