2010-09-23 8 views
17

Trình trang trí Python rất thú vị để sử dụng, nhưng tôi dường như đã va vào tường do các đối số được chuyển đến trang trí. Ở đây tôi có một trang trí được định nghĩa là một phần của một lớp cơ sở (trình trang trí sẽ truy cập các thành viên lớp do đó nó sẽ yêu cầu tham số tự).Trình trang trí Python là một phần của một lớp cơ sở không thể được sử dụng để trang trí các chức năng của thành viên trong các lớp kế thừa

class SubSystem(object): 
    def UpdateGUI(self, fun): #function decorator 
     def wrapper(*args): 
      self.updateGUIField(*args) 
      return fun(*args) 
     return wrapper 

    def updateGUIField(self, name, value): 
     if name in self.gui: 
      if type(self.gui[name]) == System.Windows.Controls.CheckBox: 
       self.gui[name].IsChecked = value #update checkbox on ui 
      elif type(self.gui[name]) == System.Windows.Controls.Slider: 
       self.gui[name].Value = value # update slider on ui 

     ... 

Tôi đã bỏ qua phần còn lại của quá trình triển khai. Bây giờ lớp này là một lớp cơ sở cho các Hệ thống con khác nhau sẽ kế thừa từ nó - một số lớp được thừa hưởng sẽ cần sử dụng trình trang trí UpdateGUI.

class DO(SubSystem): 
    def getport(self, port): 
     """Returns the value of Digital Output port "port".""" 
     pass 

    @SubSystem.UpdateGUI 
    def setport(self, port, value): 
     """Sets the value of Digital Output port "port".""" 
     pass 

Một lần nữa tôi đã bỏ qua triển khai chức năng vì chúng không liên quan.

Nói tóm lại vấn đề là trong khi tôi có thể truy cập vào trang trí được định nghĩa trong lớp cơ sở từ lớp kế thừa bởi specifiying nó như SubSystem.UpdateGUI, tôi cuối cùng có được TypeError này khi cố gắng sử dụng nó:

unbound method UpdateGUI() must be called with SubSystem instance as first argument (got function instance instead)

Điều này là do tôi không có cách nào nhận dạng được thông số self ngay lập tức cho người trang trí!

Có cách nào để thực hiện việc này không? Hay tôi đã đạt đến giới hạn của việc triển khai trang trí hiện tại bằng Python?

Trả lời

17

Bạn cần phải thực hiện UpdateGUI a @classmethod và làm cho số wrapper của mình nhận thức được self. Một ví dụ làm việc:

class X(object): 
    @classmethod 
    def foo(cls, fun): 
     def wrapper(self, *args, **kwargs): 
      self.write(*args, **kwargs) 
      return fun(self, *args, **kwargs) 
     return wrapper 

    def write(self, *args, **kwargs): 
     print(args, kwargs) 

class Y(X): 
    @X.foo 
    def bar(self, x): 
     print("x:", x) 

Y().bar(3) 
# prints: 
# (3,) {} 
# x: 3 
+0

Điều này đã hiệu quả! Không bao giờ có thể nghĩ để biến decorator thành một classmethod. – Aphex

+1

Để tham khảo trong tương lai, đây là nghĩa đen là một dòng sửa chữa bằng cách thêm @classmethod trước khi def UpdateGUI (tự, vui vẻ). – Aphex

+0

@Aphex. bạn nên thay thế việc sử dụng 'self' bằng cách sử dụng' cls' như KennyTM đã cho thấy. Điều này sẽ làm cho mã của bạn dễ đọc hơn nhiều. – aaronasterling

2

Bạn cần sử dụng phiên bản SubSystem để làm trang trí hoặc sử dụng classmethod như kenny đề xuất.

subsys = SubSystem() 
class DO(SubSystem): 
    def getport(self, port): 
     """Returns the value of Digital Output port "port".""" 
     pass 

    @subsys.UpdateGUI 
    def setport(self, port, value): 
     """Sets the value of Digital Output port "port".""" 
     pass 

Bạn quyết định để làm bằng cách quyết định nếu bạn muốn tất cả trường lớp con để chia sẻ giao diện GUI cùng hoặc nếu bạn muốn có thể để cho những người khác nhau có các giao diện khác nhau.

Nếu tất cả đều chia sẻ cùng một giao diện GUI, hãy sử dụng phương thức lớp và thực hiện mọi thứ mà trình trang trí truy cập vào một cá thể lớp.

Nếu họ có giao diện riêng biệt, bạn cần quyết định xem bạn có muốn đại diện cho sự khác biệt với thừa kế hay không (hoặc trong trường hợp này bạn cũng sử dụng classmethod và gọi người trang trí trên các lớp con của SubSystem) hoặc các trường hợp riêng biệt. Trong trường hợp đó, hãy tạo một cá thể cho mỗi giao diện và gọi trình trang trí trên cá thể đó.

+0

Nó có thể làm việc nhưng tạo ra một trường hợp singleton của lớp cơ sở chỉ để gọi trang trí là một ý tưởng tồi. Trong số những thứ khác, 'self' sẽ giới thiệu các đối tượng khác nhau bên trong trang trí và các phương pháp khác. Sau đó, bạn phải khởi tạo một đối tượng mà bạn có thể không muốn. Thay vào đó, hãy chọn các tùy chọn khác được đề xuất trong các câu trả lời khác, như định nghĩa staticmethod hoặc classmethod. – vokimon

3

Nó có thể được dễ dàng hơn để chỉ cần kéo trang trí ra khỏi lớp SubSytem: (Lưu ý rằng tôi giả định rằng self mà các cuộc gọi setport là như nhau self mà bạn muốn sử dụng để gọi updateGUIField)

def UpdateGUI(fun): #function decorator 
    def wrapper(self,*args): 
     self.updateGUIField(*args) 
     return fun(self,*args) 
    return wrapper 

class SubSystem(object): 
    def updateGUIField(self, name, value): 
     # if name in self.gui: 
     #  if type(self.gui[name]) == System.Windows.Controls.CheckBox: 
     #   self.gui[name].IsChecked = value #update checkbox on ui 
     #  elif type(self.gui[name]) == System.Windows.Controls.Slider: 
     #   self.gui[name].Value = value # update slider on ui 
     print(name,value) 

class DO(SubSystem): 
    @UpdateGUI 
    def setport(self, port, value): 
     """Sets the value of Digital Output port "port".""" 
     pass 

do=DO() 
do.setport('p','v') 
# ('p', 'v') 
+0

Chắc chắn, tôi có những người trang trí khác trong dự án của tôi chỉ cần ngồi một mình và tôi nhập chúng khi cần thiết. Tuy nhiên, vì trang trí này sử dụng một cá thể thành viên cụ thể (self.updateGUIField) nên nó phải là một phần của lớp. – Aphex

+1

@Aphex: Bạn đã thử mã của tôi chưa? Tôi nghĩ rằng nó hoạt động tốt, mà không có trang trí là một phần của lớp. – unutbu

+0

Điều này có thể làm hỏng khớp nối mà @Aphex muốn. 'UpdateGUI' không có ý nghĩa gì nếu không được gọi trong ngữ cảnh của' SubSystem' (hoặc các lớp con của 'SubSystem') vì vậy nó phải là một phương thức tĩnh hoặc lớp. – cowbert

3

bạn đã loại đã trả lời các câu hỏi trong hỏi nó:. những lập luận gì bạn mong đợi để có được càng self nếu bạn gọi SubSystem.UpdateGUI? Không có một trường hợp rõ ràng nên được chuyển cho người trang trí.

Có một số điều bạn có thể làm để giải quyết vấn đề này. Có lẽ bạn đã có một subSystem mà bạn đã khởi tạo ở một nơi khác? Sau đó, bạn có thể sử dụng trang trí của nó:

subSystem = SubSystem() 
subSystem.UpdateGUI(...) 

Nhưng có lẽ bạn không cần phải thể hiện ở nơi đầu tiên, chỉ cần lớp SubSystem? Trong trường hợp đó, sử dụng classmethod trang trí nói với Python rằng chức năng này sẽ nhận được các lớp học như là đối số đầu tiên thay vì một ví dụ:

@classmethod 
def UpdateGUI(cls,...): 
    ... 

Cuối cùng, có lẽ bạn không cần truy cập vào một trong hai trường hợp hoặc lớp ! Trong trường hợp đó, sử dụng staticmethod:

@staticmethod 
def UpdateGUI(...): 
    ... 

Oh, bằng cách này, ước Python là dự trữ tên CamelCase cho các lớp học và sử dụng mixedCase hoặc tên under_scored cho các phương pháp trên lớp đó.

+0

@Aaron: đúng, cảm ơn. – katrielalex

+0

@ self.UpdateGUI không hoạt động, vì tự không có ý nghĩa trong phạm vi của định nghĩa lớp. Đó là lý do tại sao tôi sử dụng tên của lớp cơ sở. – Aphex