Tại sao @ foo.setter trong Python không hoạt động với tôi?


155

Vì vậy, tôi đang chơi với các nhà trang trí trong Python 2.6 và tôi gặp một số khó khăn khi khiến chúng hoạt động. Đây là tập tin lớp của tôi:

class testDec:

    @property
    def x(self): 
        print 'called getter'
        return self._x

    @x.setter
    def x(self, value): 
        print 'called setter'
        self._x = value

Những gì tôi nghĩ điều này có nghĩa là đối xử xnhư một tài sản, nhưng gọi các hàm này trên get và set. Vì vậy, tôi đã kích hoạt IDLE và kiểm tra nó:

>>> from testDec import testDec
from testDec import testDec
>>> t = testDec()
t = testDec()
>>> t.x
t.x
called getter
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "testDec.py", line 18, in x
    return self._x
AttributeError: testDec instance has no attribute '_x'
>>> t.x = 5
t.x = 5
>>> t.x
t.x
5

Rõ ràng cuộc gọi đầu tiên hoạt động như mong đợi, vì tôi gọi getter, và không có giá trị mặc định, và nó thất bại. OK, tốt, tôi hiểu. Tuy nhiên, lệnh gọi để gán t.x = 5dường như tạo ra một thuộc tính mới xvà bây giờ getter không hoạt động!

Tôi đang thiếu gì?


6
Vui lòng đặt tên các lớp học với một chữ cái viết hoa. bạn có thể sử dụng chữ thường cho các biến và tệp mô-đun.
S.Lott

Câu trả lời:


306

Có vẻ như bạn đang sử dụng các lớp kiểu cũ cổ điển trong python 2. Để các thuộc tính hoạt động chính xác, bạn cần sử dụng các lớp kiểu mới thay thế (trong python 2, bạn phải kế thừa từobject ). Chỉ cần khai báo lớp của bạn là MyClass(object):

class testDec(object):

    @property
    def x(self): 
        print 'called getter'
        return self._x

    @x.setter
    def x(self, value): 
        print 'called setter'
        self._x = value

Nó hoạt động:

>>> k = testDec()
>>> k.x
called getter
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/devel/class_test.py", line 6, in x
    return self._x
AttributeError: 'testDec' object has no attribute '_x'
>>> k.x = 5
called setter
>>> k.x
called getter
5
>>> 

Một chi tiết khác có thể gây ra vấn đề là cả hai phương thức đều cần cùng tên để thuộc tính hoạt động. Nếu bạn xác định setter với một tên khác như thế này thì nó sẽ không hoạt động :

@x.setter
def x_setter(self, value):
    ...

Và một điều nữa không hoàn toàn dễ dàng nhận ra lúc đầu, đó là thứ tự: getter phải được xác định trước . Nếu bạn xác định setter trước, bạn sẽ name 'x' is not definedgặp lỗi.


4
Trong Python3, không cần thiết nữa - các lớp "cổ điển" vẫn ổn với setters :-)
Eenoku

20
Nó hoạt động trong python 3 vì mỗi lớp là một lớp kiểu mới, không còn các lớp 'cổ điển' nữa.
craigds

Tôi nghĩ người ta nên nhận được một cảnh báo / lỗi, nếu trang trí không được xử lý chính xác trong trường hợp này.
stfn

82

Chỉ cần một lưu ý cho những người khác vấp ngã ở đây tìm kiếm ngoại lệ này: cả hai chức năng cần phải có cùng tên. Đặt tên cho các phương thức như sau sẽ dẫn đến một ngoại lệ:

@property
def x(self): pass

@x.setter
def x_setter(self, value): pass

Thay vào đó hãy đặt cả hai phương thức cùng tên

@property
def x(self): pass

@x.setter
def x(self, value): pass

Cũng cần lưu ý rằng thứ tự của tờ khai có vấn đề. Getter phải được xác định trước khi setter trong tệp nếu không bạn sẽ nhận đượcNameError: name 'x' is not defined


Đây có lẽ sẽ trở thành câu trả lời hay nhất "mới", với ngày càng nhiều người trên Python 3 ...
trpt4him

5
Câu trả lời này là tuyệt vời. Tôi nghĩ rằng để được hoàn thành, sẽ thật tuyệt nếu bạn có thể nhận xét rằng thứ tự là quan trọng, nghĩa là, getter phải ở trước bộ setter trong mã. Nếu không, bạn sẽ có được một NameError: name 'x' is not definedngoại lệ.
eguaio

Cảm ơn vì điều đó. Tôi chỉ lãng phí phần tốt nhất của một ngày vào việc này. Tôi chưa bao giờ nghĩ rằng các phương pháp phải được gọi là điều tương tự. Thực sự bực bội ít thành ngữ!
ajkavanagh

Để hiểu "lý do" đằng sau câu trả lời này, hãy đọc tài liệu dứt khoát tại đây: docs.python.org/2/l Library / fiances.html #property Đặc biệt lưu ý phần "chính xác tương đương".
Evgeni Sergeev

23

Bạn cần sử dụng các lớp kiểu mới mà bạn thực hiện bằng cách lấy lớp của bạn từ đối tượng:

class testDec(object):
   ....

Sau đó, nó sẽ làm việc.


Tất cả mọi thứ ở trên đã được đặt ra. Điều này là bắt buộc đối với tôi trong python 2.7.x cho các thuộc tính trong các lớp để đá chính xác.
Drone Brain

12

Trong trường hợp bất kỳ ai đến đây từ google, ngoài các câu trả lời ở trên, tôi muốn thêm rằng điều này cần được chú ý cẩn thận khi gọi setter từ __init__phương thức của lớp bạn dựa trên câu trả lời này Cụ thể:

class testDec(object):                                                                                                                                            

    def __init__(self, value):
        print 'We are in __init__'
        self.x = value # Will call the setter. Note just x here
        #self._x = value # Will not call the setter

    @property
    def x(self):
        print 'called getter'
        return self._x # Note the _x here

    @x.setter
    def x(self, value): 
        print 'called setter'
        self._x = value # Note the _x here

t = testDec(17)
print t.x 

Output:
We are in __init__
called setter
called getter
17

Không chỉ từ __init__phương thức, mà còn sử dụng bất kỳ phương thức nào khác trong lớp.
Mia
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.