Khi nào tôi nên sử dụng @classmethod và khi nào thì phương thức def (self)?


88

Trong khi tích hợp một ứng dụng Django mà tôi chưa sử dụng trước đây, tôi đã tìm thấy hai cách khác nhau được sử dụng để xác định các hàm trong các lớp. Tác giả dường như sử dụng cả hai rất có chủ ý. Cái đầu tiên là cái mà bản thân tôi sử dụng rất nhiều:

class Dummy(object):

    def some_function(self,*args,**kwargs):
        do something here
        self is the class instance

Cái còn lại là cái mà tôi không sử dụng, chủ yếu là vì tôi không hiểu khi nào sử dụng nó và để làm gì:

class Dummy(object):

    @classmethod
    def some_function(cls,*args,**kwargs):
        do something here
        cls refers to what?

Trong tài liệu Python, trình classmethodtrang trí được giải thích bằng câu này:

Một phương thức lớp nhận lớp dưới dạng đối số ngầm đầu tiên, giống như một phương thức cá thể nhận cá thể.

Vì vậy, tôi đoán clsđề cập đến Dummychính nó ( classchứ không phải trường hợp). Tôi không hiểu chính xác tại sao điều này tồn tại, bởi vì tôi luôn có thể làm điều này:

type(self).do_something_with_the_class

Đây chỉ là vì mục đích rõ ràng, hay tôi đã bỏ lỡ phần quan trọng nhất: những điều ma quái và hấp dẫn không thể thực hiện được nếu không có nó?

Câu trả lời:


70

Phỏng đoán của bạn là đúng - bạn hiểu cách classmethod hoạt động của nó.

Lý do tại sao các phương thức này có thể được gọi cả trên một cá thể HOẶC trên lớp (trong cả hai trường hợp, đối tượng lớp sẽ được truyền làm đối số đầu tiên):

class Dummy(object):

    @classmethod
    def some_function(cls,*args,**kwargs):
        print cls

#both of these will have exactly the same effect
Dummy.some_function()
Dummy().some_function()

Về việc sử dụng chúng trên các phiên bản : Có ít nhất hai cách sử dụng chính để gọi một phương thức phân lớp trên một cá thể:

  1. self.some_function()sẽ gọi phiên bản của some_functionkiểu thực tế self, chứ không phải là lớp trong đó lệnh gọi đó xuất hiện (và sẽ không cần chú ý nếu lớp được đổi tên); và
  2. Trong những trường hợp some_functioncần thiết để triển khai một số giao thức, nhưng hữu ích khi gọi đối tượng lớp một mình.

Sự khác biệt vớistaticmethod : Có một cách khác để xác định các phương thức không truy cập dữ liệu cá thể, được gọi staticmethod. Điều đó tạo ra một phương thức không nhận được đối số đầu tiên ngầm định nào cả; theo đó nó sẽ không được chuyển bất kỳ thông tin nào về cá thể hoặc lớp mà nó được gọi.

In [6]: class Foo(object): some_static = staticmethod(lambda x: x+1)

In [7]: Foo.some_static(1)
Out[7]: 2

In [8]: Foo().some_static(1)
Out[8]: 2

In [9]: class Bar(Foo): some_static = staticmethod(lambda x: x*2)

In [10]: Bar.some_static(1)
Out[10]: 2

In [11]: Bar().some_static(1)
Out[11]: 2

Công dụng chính mà tôi đã tìm thấy cho nó là điều chỉnh một hàm hiện có (không mong đợi nhận được a self) thành một phương thức trên một lớp (hoặc đối tượng).


2
Cái hay: Tôi thích câu trả lời được chia thành cách - tại sao. Theo như những gì tôi có thì bây giờ @classmethod cho phép truy cập hàm mà không cần phiên bản. Đây chính xác là những gì tôi đang tìm kiếm, cảm ơn bạn.
marue

1
@marcin Kiểu gõ vịt đúng mang lại cho nó một số khía cạnh khác. Đó là nhiều hơn về việc không viết những thứ như self.foo()khi nó nên được Bar.foo().
Voo

5
@Voo Trên thực tế, self.foo()tốt hơn, vì selfcó thể là một phiên bản của một lớp con triển khai riêng của nó foo.
Marcin

1
@Marcin Tôi nghĩ điều này phụ thuộc vào những gì bạn muốn đạt được. Nhưng tôi hiểu ý bạn.
marue

1
Bạn nên sử dụng một lambda khác cho Bar, 1+1 == 2 == 1*2vì vậy không thể phân biệt được từ kết quả hiển thị nó Bar().static_methodthực sự được gọi.
George V. Reilly

0

Về cơ bản, bạn nên sử dụng @classmethod khi bạn nhận ra rằng định nghĩa của phương thức sẽ không bị thay đổi hoặc ghi đè.

Một bổ sung: về cơ bản, các phương thức lớp nhanh hơn các phương thức đối tượng, bởi vì không cần khởi tạo và cần ít bộ nhớ hơn.


-3

Nếu bạn thêm decorator @classmethod, điều đó có nghĩa là bạn sẽ đặt phương thức đó làm phương thức tĩnh của java hoặc C ++. (static method là một thuật ngữ chung mà tôi đoán ;) ) Python cũng có @staticmethod. và sự khác biệt giữa classmethod và staticmethod là liệu bạn có thể truy cập vào lớp hoặc biến tĩnh bằng cách sử dụng đối số hoặc chính tên lớp hay không.

class TestMethod(object):
    cls_var = 1
    @classmethod
    def class_method(cls):
        cls.cls_var += 1
        print cls.cls_var

    @staticmethod
    def static_method():
        TestMethod.cls_var += 1
        print TestMethod.cls_var
#call each method from class itself.
TestMethod.class_method()
TestMethod.static_method()

#construct instances
testMethodInst1 = TestMethod()    
testMethodInst2 = TestMethod()   

#call each method from instances
testMethodInst1.class_method()
testMethodInst2.static_method()

tất cả các lớp đó tăng cls.cls_var lên 1 và in nó.

Và mọi lớp sử dụng cùng một tên trên cùng một phạm vi hoặc các trường hợp được xây dựng với lớp này sẽ chia sẻ các phương thức đó. Chỉ có một TestMethod.cls_var và cũng chỉ có một TestMethod.class_method (), TestMethod.static_method ()

Và câu hỏi quan trọng. tại sao phương pháp này lại cần thiết.

classmethod hoặc staticmethod hữu ích khi bạn tạo lớp đó dưới dạng nhà máy hoặc khi bạn chỉ phải khởi tạo lớp của mình một lần. như mở tệp một lần và sử dụng phương thức nguồn cấp dữ liệu để đọc từng dòng tệp.


2
(a) Phương thức tĩnh là một cái gì đó khác; (b) các classmethods không được chia sẻ bởi mọi lớp.
Marcin

@Marcin cảm ơn vì đã cho tôi biết các định nghĩa không rõ ràng của tôi và tất cả những điều đó.
Ryan Kim
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.