2013-04-26 40 views
5

Tại sao nó rằng trong đoạn mã sau, sử dụng một biến lớp học như một kết quả con trỏ phương pháp do lỗi phương pháp cởi ra, khi sử dụng một biến bình thường hoạt động tốt:Con trỏ tới phương pháp tĩnh trong Python

class Cmd: 
    cmd = None 

    @staticmethod 
    def cmdOne(): 
     print 'cmd one' 

    @staticmethod 
    def cmdTwo(): 
     print 'cmd two' 

def main(): 
    cmd = Cmd.cmdOne 
    cmd() # works fine 

    Cmd.cmd = Cmd.cmdOne   
    Cmd.cmd() # unbound error !! 

if __name__=="__main__": 
    main() 

Các lỗi đầy đủ :

TypeError: unbound method cmdOne() must be called with Cmd instance as 
      first argument (got nothing instead) 

Trả lời

2

Tôi thích xem hành vi này từ "từ dưới lên".

Hàm trong Python hoạt động như "descriptor object". Như vậy, nó có phương thức __get__().

Quyền truy cập đọc vào thuộc tính lớp có phương thức __get__() được chuyển hướng đến phương thức này. Quyền truy cập thuộc tính vào lớp được thực hiện dưới dạng attribute.__get__(None, containing_class), trong khi quyền truy cập thuộc tính đối với cá thể được ánh xạ tới attribute.__get__(instance, containing_class).

Nhiệm vụ của phương thức __get__() của hàm là bao bọc hàm trong đối tượng phương thức bao bọc thông số self - đối với trường hợp truy cập thuộc tính đối tượng. Điều này được gọi là một phương pháp ràng buộc.

Trên truy cập thuộc tính lớp trên 2.x, hàm __get__() của hàm trả về trình bao bọc phương thức không gắn kết, trong khi, như tôi learned today, trên 3.x, nó tự trả về. (Lưu ý rằng cơ chế __get__() vẫn tồn tại trong 3.x, nhưng một hàm chỉ trả về chính nó.) Đó là gần như giống nhau, nếu bạn nhìn vào cách nó được gọi, nhưng một wrapper phương pháp unbound bổ sung kiểm tra cho các loại chính xác của đối số self.

Cuộc gọi staticmethod() chỉ tạo đối tượng có lệnh gọi __get__() được thiết kế để trả lại đối tượng ban đầu đã cho để nó hoàn tác hành vi được mô tả. Đó là cách HYRY's trick hoạt động: thuộc tính acces undoes gói staticmethod(), cuộc gọi lại thực hiện để thuộc tính "mới" có cùng trạng thái như cũ, mặc dù trong trường hợp này, staticmethod() dường như được áp dụng hai lần (nhưng thực sự isn ' t).

(BTW: Nó còn hoạt động trong bối cảnh kỳ lạ này:.

s = staticmethod(8) 
t = s.__get__(None, 2) # gives 8 

mặc dù 8 không phải là một chức năng và 2 không phải là một lớp)

Trong câu hỏi của bạn, bạn có hai tình huống:

cmd = Cmd.cmdOne 
cmd() # works fine 

truy cập lớp học và yêu cầu thuộc tính cmdOne, đối tượng staticmethod(). Điều này được truy vấn thông qua __get__() của nó và trả về hàm ban đầu, sau đó được gọi. Đó là lý do tại sao nó hoạt động tốt.

Cmd.cmd = Cmd.cmdOne 
Cmd.cmd() # unbound error 

làm tương tự, nhưng sau đó gán chức năng này cho Cmd.cmd. Dòng tiếp theo là một truy cập thuộc tính - một lần nữa, cuộc gọi __get__() đến chính hàm đó và do đó trả về một phương thức không liên kết, phải được gọi với đối tượng self chính xác làm đối số đầu tiên.

3

Bạn cần phải sử dụng staticmethod() để chuyển đổi chức năng:

Cmd.cmd = staticmethod(Cmd.cmdOne) 
+0

Không trả lời câu hỏi. – martineau

3

Y đang chạy vào hành vi của "các phương thức không liên kết" trong Python 2.x. Về cơ bản, trong Python 2.x, khi bạn nhận được một thuộc tính của một lớp (ví dụ trong trường hợp này là Cmd.cmd), và giá trị là một hàm, thì lớp "kết thúc" hàm thành một đối tượng "phương pháp không ràng buộc" đặc biệt, vì chúng giả sử rằng các thuộc tính của các lớp là các hàm và không được trang trí với staticmethod hoặc classmethod có nghĩa là các phương thức thể hiện (một giả định không chính xác trong trường hợp này). Phương thức không liên kết này mong đợi một đối số khi được gọi, mặc dù trong trường hợp này hàm cơ bản không mong đợi một đối số.

Hành vi này được giải thích trong language reference:

(trong "Lớp học" phần)

When a class attribute reference (for class C, say) would yield a user-defined function object or [...], it is transformed into an unbound user-defined method object whose im_class attribute is C.

(trong "phương pháp dùng định nghĩa" phần)

When a user-defined method object is created by retrieving a user-defined function object from a class, its im_self attribute is None and the method object is said to be unbound.

[ ...]

When an unbound user-defined method object is called, the underlying function (im_func) is called, with the restriction that the first argument must be an instance of the proper class (im_class) or of a derived class thereof.

Đó là những gì gây ra lỗi mà bạn đang gặp phải.

Bạn có thể lấy một cách rõ ràng các chức năng cơ bản ra khỏi đối tượng phương pháp và gọi đó (nhưng nó rõ ràng không phải lý tưởng cần phải làm điều này):

Cmd.cmd.im_func() 

Lưu ý rằng Python 3.x got rid of unbound methods và mã của bạn sẽ chạy tốt trên Python 3.x

+0

Để chính xác, chính bản thân hàm này tự "kết thúc" thành đối tượng "không liên kết": bằng cách truy cập nó, lớp gọi hàm '__get __()' của hàm tạo ra trình bao bọc. – glglgl

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