2012-01-14 34 views
6

Tôi đã làm việc trên một kịch bản đơn giản ngày hôm nay khi tôi nhận thấy một quirk lạ trong cách Python xử lý các biến mẫu.Tại sao Python dường như coi các biến mẫu như được chia sẻ giữa các đối tượng?

Giả sử chúng ta có một đối tượng đơn giản:

class Spam(object): 
    eggs = {} 

    def __init__(self, bacon_type): 
     self.eggs["bacon"] = bacon_type 

    def __str__(self): 
     return "My favorite type of bacon is " + self.eggs["bacon"] 

Và chúng ta tạo ra hai trường hợp của đối tượng này với đối số riêng biệt:

spam1 = Spam("Canadian bacon") 
spam2 = Spam("American bacon") 

print spam1 
print spam2 

Các kết quả được đánh đố:

My favorite type of bacon is American bacon 
My favorite type of bacon is American bacon 

Nó có vẻ như từ điển "trứng" được chia sẻ giữa tất cả các phiên bản "Spam" khác nhau - hoặc là ghi đè mỗi khi một cá thể mới được tạo ra. Đây không phải là thực sự là một vấn đề trong cuộc sống hàng ngày, vì chúng ta có thể giải quyết nó bằng cách tuyên bố biến dụ trong hàm khởi tạo:

class Spam(object): 

    def __init__(self, bacon_type): 
     self.eggs = {} 
     self.eggs["bacon"] = bacon_type 

    def __str__(self): 
     return "My favorite type of bacon is " + self.eggs["bacon"] 

spam1 = Spam("Canadian bacon") 
spam2 = Spam("American bacon") 

print spam1 
print spam2 

Với mã bằng văn bản theo cách này, kết quả là những gì chúng ta mong đợi:

My favorite type of bacon is Canadian bacon 
My favorite type of bacon is American bacon 

Vì vậy, trong khi tôi không bị giữ bởi hành vi này, tôi không hiểu tại sao Python hoạt động theo cách này. Bất cứ ai có thể làm sáng tỏ về điều này?

+0

bản sao có thể có của [Làm thế nào để tránh có dữ liệu lớp Python được chia sẻ giữa các trường hợp?] (Http://stackoverflow.com/questions/1680528/how-do-i-avoid-having-python-class-data-shared -among-instances) –

Trả lời

7

Khi Ignacio đã đăng, các biến được gán cho phạm vi lớp trong Python là các biến lớp. Về cơ bản, trong Python, một lớp chỉ là một danh sách các câu lệnh theo câu lệnh class. Khi danh sách các câu lệnh kết thúc thực hiện, Python sẽ dò tìm bất kỳ biến nào đã được tạo ra trong quá trình thực thi đó và tạo ra một lớp trong đó. Nếu bạn muốn một biến cá thể, bạn thực sự phải gán nó cho cá thể.

Trên một lưu ý khác: có vẻ như bạn có thể đến điều này từ phối cảnh Java (hoặc giống Java). Vì vậy, có lẽ bạn biết rằng vì Java yêu cầu các biến được khai báo một cách rõ ràng, nó cần phải có các khai báo biến cá thể ở phạm vi lớp.

class Foo { 
    String bar; 
    public Foo() { 
     this.bar = "xyz"; 
    } 
} 

Lưu ý rằng chỉ có khai báo là ở phạm vi lớp học. Nói cách khác, bộ nhớ được phân bổ cho biến đó là một phần của "mẫu" lớp học, nhưng giá trị thực tế của biến này thì không.

Python không cần bất kỳ khai báo biến nào. Vì vậy, trong bản dịch Python, bạn chỉ cần thả khai báo.

class Foo: 
    # String bar; <-- useless declaration is useless 
    def __init__(self): 
     self.bar = "xyz" 

Bộ nhớ sẽ được phân bổ khi cần thiết; chỉ có bài tập thực sự được viết ra. Và điều đó xảy ra trong hàm tạo, giống như trong Java.

+0

Vâng, chính xác - khi tôi học OOP ở trường đại học, chúng tôi đã làm điều đó trong Java. –

5

Đó không phải là biến mẫu, đó là biến lớp. Thực tế là nó đang được truy cập thông qua một cá thể là không liên quan; nó vẫn là cùng một đối tượng.

+0

Tôi nhận được những gì bạn đang nói. Nhưng, trong khi xác định một lớp, làm thế nào để bạn khai báo các biến như các biến mẫu hoặc các biến lớp? Tôi có nghĩa là làm thế nào bạn biết nó là một biến lớp khi bạn nhìn vào mã này? – Divya

+2

Đó là một phần của định nghĩa lớp, tức là 'Spam.eggs'. Khi bị ràng buộc như một thuộc tính của 'self', nó không phải là một thuộc tính của lớp mà là một cá thể. –

+0

Ok, vì vậy nếu tôi hiểu bạn một cách chính xác, một khi tôi đã tạo ra một cá thể Spam, tôi có thể gọi Spam.eggs trực tiếp và lấy trứng của cá thể Spam mà tôi vừa tạo ra? Cảm ơn bạn đã giúp đỡ! –

2

Không giống như trong ngôn ngữ được biên dịch, câu lệnh class bằng Python thực sự là mã thực thi và tạo đối tượng lớp (không phải là một thể hiện!) Trong bộ nhớ.

Bất kỳ ký hiệu nào được xác định khi chạy khối class thuộc về chính lớp đó.Điều này bao gồm các biến, chẳng hạn như biến số eggs trong ví dụ đầu tiên của bạn, cũng như các phương thức __init____str__ mà bạn xác định. Tất cả chúng được tạo ra khi lớp được định nghĩa và tất cả chúng đều là một phần của lớp.

Biến cá thể không được tạo cho đến khi bạn thực sự tạo đối tượng của đối tượng và phương thức __init__ được chạy và chúng là thuộc tính của self.

Vì vậy, khi người phiên dịch python thực thi

class Spam(object): 
    eggs = {} 

    def __init__(self): 
     <stuff> 
    def __str__(self): 
     <other stuff> 

nó thực sự đang xây dựng một đối tượng lớp tại thời gian chạy. Nó thực thi mã "eggs={}" và nó thực thi hai câu lệnh def và nó tạo một lớp có ba thuộc tính: eggs, __init____str__.

Sau đó, khi nó thực thi

spam1 = Spam() 

Sau đó, nó tạo ra một thể hiện mới, và chạy phương pháp __init__ của nó. Dĩ nhiên, phương thức __init__ là thuộc về lớp; nó được chia sẻ giữa tất cả các trường hợp, giống như thuộc tính eggs.

Bản thân thể hiện được chuyển thành tham số self và mọi thứ bạn xác định trên thông số đó chỉ thuộc về cá thể đó. Đó là lý do tại sao self phải được chuyển vào mọi phương thức lớp - trong python, các phương thức thực sự thuộc về chính lớp đó và self là cách duy nhất mà bạn phải tham chiếu đến cá thể đó.

Các vấn đề liên quan