2013-03-22 31 views
5

Tôi đang gặp khó khăn khi hiểu những gì sẽ xảy ra khi tôi cố gắng lồng ghép các bộ mô tả/trang trí. Tôi đang sử dụng python 2.7.Các bộ mô tả/trang trí làm tổ trong python

Ví dụ, chúng ta hãy phiên bản đơn giản sau đây của propertyclassmethod:

class MyProperty(object): 
    def __init__(self, fget): 
     self.fget = fget 
    def __get__(self, obj, objtype=None): 
     print 'IN MyProperty.__get__' 
     return self.fget(obj) 

class MyClassMethod(object): 
    def __init__(self, f): 
     self.f = f 
    def __get__(self, obj, objtype=None): 
     print 'IN MyClassMethod.__get__' 
     def f(*args, **kwargs): 
      return self.f(objtype, *args, **kwargs) 
     return f 

Đang cố gắng để tổ họ:

class A(object): 
    # doesn't work: 
    @MyProperty 
    @MyClassMethod 
    def klsproperty(cls): 
     return 555 
    # works: 
    @MyProperty 
    def prop(self): 
     return 111 
    # works: 
    @MyClassMethod 
    def klsmethod(cls, x): 
     return x**2 

% print A.klsproperty 
IN MyProperty.__get__ 
... 
TypeError: 'MyClassMethod' object is not callable 

Phương pháp __get__ của bộ mô tả nội MyClassMethod không nhận được gọi là. Không hiểu tại sao, tôi đã cố gắng ném trong (những gì tôi nghĩ là) một mô tả không-op:

class NoopDescriptor(object): 
    def __init__(self, f): 
     self.f = f 
    def __get__(self, obj, objtype=None): 
     print 'IN NoopDescriptor.__get__' 
     return self.f.__get__(obj, objtype=objtype) 

Cố gắng sử dụng các không-op mô tả/trang trí trong tổ:

class B(object): 
    # works: 
    @NoopDescriptor 
    @MyProperty 
    def prop1(self): 
     return 888 
    # doesn't work: 
    @MyProperty 
    @NoopDescriptor 
    def prop2(self): 
     return 999 

% print B().prop1 
IN NoopDescriptor.__get__ 
IN MyProperty.__get__ 
888 
% print B().prop2 
IN MyProperty.__get__ 
... 
TypeError: 'NoopDescriptor' object is not callable 

Tôi không hiểu tại sao B().prop1 hoạt động và B().prop2 thì không.

Câu hỏi:

  1. Tôi đang làm gì sai? Tại sao tôi gặp lỗi object is not callable?
  2. Cách phù hợp là gì? ví dụ. những gì là cách tốt nhất để xác định MyClassProperty khi tái sử dụng MyClassMethodMyProperty (hoặc classmethodproperty)
+1

Chìa khóa để hiểu trực giác này là '@ MyProperty' tạo ra thứ gì đó hoạt động giống như thuộc tính _data_, không phải là phương thức, do đó bạn không thể lồng nó bằng bộ mô tả phương thức. Sự hiểu biết trực quan đó chỉ giúp bạn cho đến nay; câu chuyện đầy đủ nằm trong câu trả lời của isedev. – abarnert

Trả lời

3

Bạn có thể làm cho công việc mã của bạn nếu bạn thực hiện MyProperty áp dụng các giao thức mô tả đối tượng bọc của nó:

class MyProperty(object): 
    def __init__(self, fget): 
     self.fget = fget 
    def __get__(self, obj, objtype=None): 
     print('IN MyProperty.__get__') 
     try: 
      return self.fget.__get__(obj, objtype)() 
     except AttributeError: # self.fget has no __get__ method 
      return self.fget(obj) 

Bây giờ mã ví dụ của bạn hoạt động:

class A(object): 
    @MyProperty 
    @MyClassMethod 
    def klsproperty(cls): 
     return 555 

print(A.klsproperty) 

Đầu ra là:

IN MyProperty.__get__ 
IN MyClassMethod.__get__ 
555 
+0

Cảm ơn, điều này có ý nghĩa. Nhưng đây có phải là phương pháp được khuyến nghị không? Nếu vậy, làm thế nào đến 'nội trang' và 'classmethod' được xây dựng không hoạt động theo cách này (và không thể lồng nhau)? – shx2

+0

Tôi không chắc chắn nếu điều này là nản lòng, hoặc lý do tại sao 'tài sản' dựng sẵn không làm điều này rồi. Tôi biết rằng có một số hạn chế mà điều này không sửa chữa mặc dù. Đối với một điều, bạn không thể lồng các mô tả theo thứ tự ngược lại, ngay cả khi bạn đã làm cho 'MyClassMethod' trở nên thông minh hơn. Và tôi không nghĩ rằng chức năng mô tả '__set__' được gọi cho phép gán biến lớp, vì vậy bạn cần phải thực hiện một số phép thuật metaclass cho các classproperties có thể ghi được để làm việc.Có lẽ ai đó có nhiều kinh nghiệm lập trình mô tả hơn có thể cân nhắc về các vấn đề và giới hạn khác. – Blckknght

5

Trong trường hợp này, khi trang trí được sử dụng không có tham số, một trang trí được gọi với chức năng nó trang trí như nó tham số. Giá trị trả về của trang trí được sử dụng thay cho chức năng được trang trí. Vì vậy:

@MyProperty 
def prop(self): 
    ... 

tương đương với:

def prop(self): 
    ... 
prop = MyProperty(prop) 

Kể từ MyProperty thực hiện các giao thức mô tả, truy cập A.prop thực sự sẽ gọi A.prop.__get__(), và bạn đã xác định __get__ để gọi các đối tượng được trang trí (trong này trường hợp, hàm/phương thức gốc), vì vậy mọi thứ hoạt động tốt.

Bây giờ, trong trường hợp lồng nhau:

@MyProperty 
@MyClassMethod 
def prop(self): 
    ... 

Tương đương là:

def prop(self): 
    ... 
prop = MyClassMethod(prop) # prop is now instance of MyClassMethod 
prop = MyProperty(prop)  # prop is now instance of MyProperty 
          # (with fget == MyClassMethod instance) 

Bây giờ, như trước đây, việc truy cập A.prop sẽ thực sự gọi A.prop.__get__() (trong MyProperty) mà sau đó cố gắng cuộc gọi ví dụ của MyClassMethod (đối tượng được trang trí và lưu trữ trong thuộc tính fget).

Nhưng MyClassMethod không có phương thức __call__ được xác định, do đó bạn gặp lỗi MyClassMethod is not callable.


Và để giải quyết câu hỏi thứ hai của bạn: Một tài sản đã là một thuộc tính lớp - trong ví dụ của bạn, truy cập A.prop sẽ trả về giá trị của tài sản trong đối tượng lớp và A().prop sẽ trả về giá trị của tài sản trong một instance object (có thể giống với đối tượng class nếu instance không ghi đè lên nó).

+0

Giải thích của bạn nếu rõ ràng. Cảm ơn bạn. Về câu trả lời thứ hai: Tôi không chắc tôi hiểu ý bạn là gì. Tôi đang tìm một cái gì đó song song với 'classmethod', có nghĩa là tôi có thể gọi nó là 'A.prop' hoặc' A(). Prop', và trong cả hai trường hợp, hàm cơ bản được truyền vào một 'cls' arg. – shx2

+0

Trong trường hợp đó, bạn có thể kiểm tra kiểu đối số 'obj': nếu đó là một cá thể lớp, bạn có thể trích xuất lớp từ nó và chuyển nó tới hàm được trang trí thay vì đối tượng thể hiện. – isedev

+0

Bạn đang thiếu điểm ... Câu hỏi của tôi không phải là về cách triển khai trình trang trí 'classproperty'. Đó là về các mô tả làm tổ. Logic bạn đề xuất đã được thực hiện trong 'classmethod'. Tôi hy vọng sẽ có một cách sạch sẽ để tái sử dụng/kết hợp 'property' và' classmethod' để đạt được điều đó, thay vì thực hiện lại logic trong chúng. Nhưng tôi bắt đầu nghĩ rằng không có ... – shx2

0

Tôi tìm thấy câu trả lời cuối cùng cho câu hỏi cũ của riêng mình trong Graham Dumpl eton hấp dẫn của blog.

Tóm lại, trang trí tôi đã viết không tôn vinh những mô tả giao thức, bằng cách cố gắng để gọi hàm bọc/đối tượng trực tiếp, thay vì đầu tiên đem lại cho họ một cơ hội để thực hiện "mô tả ma thuật" của họ (bằng cách gọi __get__() của họ Đầu tiên).

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