Lợi thế của việc sử dụng các phương thức tĩnh trong Python là gì?


90

Tôi gặp phải lỗi phương thức không liên kết trong python với mã

import random

class Sample(object):
'''This class defines various methods related to the sample'''

    def drawSample(samplesize,List):
        sample=random.sample(List,samplesize)
        return sample

Choices=range(100)
print Sample.drawSample(5,Choices)

Sau khi đọc nhiều bài viết hữu ích ở đây, tôi đã tìm ra cách tôi có thể thêm @staticmethodở trên để mã hoạt động. Tôi là một người mới chơi trăn. Ai đó có thể vui lòng giải thích lý do tại sao người ta muốn xác định các phương thức tĩnh không? Hoặc, tại sao không phải tất cả các phương thức được định nghĩa là phương thức tĩnh?


1
Đây là một câu hỏi kỳ quặc. Phương pháp tĩnh là một thiết kế cần thiết . Nó không phải là một điều "lợi thế". Bạn sử dụng chúng bởi vì bạn phải. Đó là một tính năng thiết kế của một lớp để có các phương thức tĩnh. Bạn đang hỏi những phương thức tĩnh nào ở vị trí đầu tiên? Tôi nghĩ câu hỏi có thể được sửa lại để xác định rõ ràng hơn những gì bạn cần biết.
S.Lott

15
Không, tôi không muốn biết chúng là gì. Điều tôi muốn biết là tại sao nó lại là "điều cần thiết", điều này đã trở nên rõ ràng từ những câu trả lời của những người khác. Đó là khi bạn định nghĩa nó thay vì các phương thức không tĩnh. Cảm ơn.
Curious2learn

3
@ S.Lott: Khi nào thì việc sử dụng staticmethod là điều cần thiết thay vì sử dụng phương thức lớp bình thường? Theo như tôi có thể nói, một phương thức lớp có thể làm mọi thứ mà một staticmethod có thể. Staticmethod có "ưu điểm" như được liệt kê ở những nơi khác trong bài đăng này, nhưng tôi không thể thấy bất kỳ lý do nào tại sao một phương thức lớp không thể được sử dụng ở bất kỳ nơi nào mà một phương thức tĩnh có thể được sử dụng, do đó làm cho nó trở nên cần thiết.
RFV

Câu trả lời:


128

Các phương thức static bị hạn chế sử dụng vì chúng không có quyền truy cập vào các thuộc tính của một thể hiện của một lớp (giống như một phương thức thông thường) và chúng không có quyền truy cập vào các thuộc tính của chính lớp đó (như một phương thức lớp ).

Vì vậy, chúng không hữu ích cho các phương pháp hàng ngày.

Tuy nhiên, chúng có thể hữu ích khi nhóm một số chức năng tiện ích cùng với một lớp - ví dụ như một chuyển đổi đơn giản từ kiểu này sang kiểu khác - không cần truy cập vào bất kỳ thông tin nào ngoài các tham số được cung cấp (và có lẽ một số thuộc tính chung cho mô-đun). )

Chúng có thể được đặt bên ngoài lớp, nhưng nhóm chúng bên trong lớp có thể có ý nghĩa khi chúng chỉ có thể áp dụng ở đó.

Bạn cũng có thể tham chiếu phương thức thông qua một trường hợp hoặc lớp, thay vì tên mô-đun, điều này có thể giúp người đọc hiểu phương thức có liên quan đến trường hợp nào.


8
@ Curious2learn: Không phải tất cả , nhưng một số phương thức hữu ích như phương thức tĩnh. Hãy nghĩ về một Localelớp ví dụ , các thể hiện của nó sẽ là miền địa phương (duh). Một getAvailableLocales()phương thức sẽ là một ví dụ hay về một phương thức tĩnh của lớp như vậy: nó rõ ràng thuộc về lớp Locale, trong khi rõ ràng không thuộc bất kỳ trường hợp cụ thể nào.
MestreLion

... và nó cũng không phải là một phương thức của lớp, vì nó có thể không cần truy cập vào bất kỳ phương thức nào của lớp.
MestreLion

Một trình soạn thảo chỉ ra rằng một phương thức tĩnh có thể truy cập các thuộc tính của lớp, bằng cách điều hướng rõ ràng xuống từ class_name.attribute, chỉ không cls.attributehoặc self.attribute. Điều này đúng với các thuộc tính "công khai". Theo quy ước, bạn không nên truy cập các thuộc tính bị ẩn với dấu gạch dưới hoặc các tên có hai dấu gạch dưới theo cách này. Nó cũng có nghĩa là mã của bạn sẽ dễ hỏng hơn khi các thuộc tính thay đổi vị trí trên hệ thống phân cấp kế thừa.
Oddthinking

Một trích dẫn từ Guido giúp quyết định thời điểm sử dụng staticmethods (không bao giờ): "Tất cả chúng ta đều biết các phương thức tĩnh bị giới hạn như thế nào. (Về cơ bản chúng chỉ là một sự tình cờ - trở lại Python 2.2 ngày khi tôi phát minh ra các lớp và bộ mô tả kiểu mới , Tôi có ý định triển khai các phương thức lớp nhưng lúc đầu tôi không hiểu chúng và vô tình triển khai các phương thức tĩnh trước. Sau đó, đã quá muộn để xóa chúng và chỉ cung cấp các phương thức lớp. "
Boris Churzin

188

Xem bài viết này để giải thích chi tiết.

TL; DR

1. Nó loại bỏ việc sử dụng selfđối số.

2. Nó làm giảm mức sử dụng bộ nhớ vì Python không phải khởi tạo một phương thức giới hạn cho mỗi đối tượng được kích hoạt:

>>>RandomClass().regular_method is RandomClass().regular_method
False
>>>RandomClass().static_method is RandomClass().static_method
True
>>>RandomClass.static_method is RandomClass().static_method
True

3. Nó cải thiện khả năng đọc mã, biểu thị rằng phương thức không phụ thuộc vào trạng thái của chính đối tượng.

4. Nó cho phép ghi đè phương thức trong đó nếu phương thức được định nghĩa ở cấp mô-đun (tức là bên ngoài lớp) thì một lớp con sẽ không thể ghi đè phương thức đó.


3
Đây phải là câu trả lời được chấp nhận. Thực tế là phương thức tĩnh trên bất kỳ trường hợp nào và bản thân lớp là cùng một đối tượng là một lợi thế thực sự, đặc biệt là khi bạn có nhiều trường hợp (ví dụ: mỗi trường hợp cho một bản ghi cơ sở dữ liệu có thể thay đổi).
Zhuoyun Wei

+1 và đồng ý với @ZhuoyunWei. Chỉ có câu trả lời giải thích một số lý do tại sao một phương thức tĩnh đôi khi được ưa chuộng hơn một phương thức lớp (mặc dù 1 và 3 thực sự là cùng một lý do).
Alex

1
Câu trả lời tốt nhất cho "tại sao phương thức tĩnh", nhưng câu hỏi ban đầu cũng được hỏi "tại sao tất cả các phương thức không phải là tĩnh?". "Không có quyền truy cập vào thuộc tính phiên bản" ngắn cũng sẽ bao gồm điều đó.
Exu

Ưu điểm thực sự duy nhất của IMO là bạn có thể ghi đè nó trong một lớp con, nhưng ngay cả khi điều đó vẫn có cảm giác rằng bạn đang làm sai điều gì đó OOP khôn ngoan, bạn sẽ làm điều đó trong trường hợp sử dụng nào?
Boris Churzin

23

Đây không phải là vấn đề thực sự của câu hỏi của bạn, nhưng vì bạn đã nói rằng bạn là một người mới chơi python, có lẽ nó sẽ hữu ích và không ai khác đã đứng ra và nói điều đó một cách rõ ràng.

Tôi sẽ không bao giờ sửa đoạn mã trên bằng cách đặt phương thức này thành phương thức tĩnh. Tôi có thể đã bỏ lớp và chỉ viết một hàm:

def drawSample(samplesize,List):
    sample=random.sample(List,samplesize)
    return sample

Choices=range(100)
print drawSample(5,Choices)

Nếu bạn có nhiều chức năng liên quan, bạn có thể nhóm chúng trong một mô-đun - tức là, đặt tất cả chúng trong cùng một tệp, được đặt tên sample.pychẳng hạn; sau đó

import sample

Choices=range(100)
print sample.drawSample(5,Choices)

Hoặc tôi đã thêm một __init__phương thức vào lớp và tạo một thể hiện có các phương thức hữu ích:

class Sample(object):
'''This class defines various methods related to the sample'''

    def __init__(self, thelist):
        self.list = thelist

    def draw_sample(self, samplesize):
        sample=random.sample(self.list,samplesize)
        return sample

choices=Sample(range(100))
print choices.draw_sample(5)

(Tôi cũng đã thay đổi các quy ước trường hợp trong ví dụ trên để phù hợp với phong cách được khuyến nghị bởi PEP 8.)

Một trong những lợi thế của Python là nó không bắt bạn phải sử dụng các lớp cho mọi thứ. Bạn chỉ có thể sử dụng chúng khi có dữ liệu hoặc trạng thái cần được liên kết với các phương thức, đó là những gì các lớp dành cho. Nếu không, bạn có thể sử dụng các hàm, đó là chức năng dùng để làm gì.


Cảm ơn đã nhận xét. Tôi thực sự cần một lớp học trong trường hợp này vì tôi muốn làm việc với mẫu đã vẽ. Không, tôi không sử dụng phương pháp tĩnh, nhưng muốn tìm hiểu về nó, vì tôi đã gặp thuật ngữ này khi tra cứu thông tin về thông báo lỗi mà tôi nhận được. Nhưng lời khuyên của bạn về việc thu thập các chức năng vào một mô-đun mà không xác định lớp sẽ hữu ích cho các chức năng khác mà tôi cần. Vì vậy, cảm ơn.
Curious2learn

4
+1: Đó là một lời giải thích hay về một số quan niệm sai lầm mà OP đã mắc phải. Và bạn đã rất thành thật khi nói thẳng rằng bạn không thực sự trả lời câu hỏi của anh ấy về các phương thức tĩnh, nhưng bạn đã đưa ra một giải pháp tốt hơn nhiều khi nói rằng vấn đề của anh ấy không yêu cầu các lớp học.
MestreLion

22

Tại sao người ta muốn định nghĩa các phương thức tĩnh ?

Giả sử chúng ta có một classgọi Mathsau đó

ai sẽ muốn tạo đối tượng của class Math
và sau đó gọi các method như ceilfloorfabstrên đó.

Vì vậy, chúng tôi làm cho chúng static.

Ví dụ làm

>> Math.floor(3.14)

tốt hơn nhiều so với

>> mymath = Math()
>> mymath.floor(3.14)

Vì vậy, chúng hữu ích theo một cách nào đó. Bạn không cần tạo một thể hiện của một lớp để sử dụng chúng.

Tại sao không phải tất cả các phương thức đều được định nghĩa là phương thức tĩnh ?

Họ không có quyền truy cập vào các biến cá thể.

class Foo(object):
    def __init__(self):
        self.bar = 'bar'

    def too(self):
        print self.bar

    @staticmethod
    def foo():
        print self.bar

Foo().too() # works
Foo.foo() # doesn't work

Đó là lý do tại sao chúng tôi không làm cho tất cả các phương thức tĩnh.


10
Nhưng tại sao không phải là một gói toán? Python có các gói cho điều đó, bạn không cần định nghĩa lớp để tạo không gian tên.
extraneon

6
@extraneon: Đúng vậy, tôi biết điều đó nhưng tôi muốn có một cái gì đó đơn giản và quen thuộc để giải thích nên tôi đã sử dụng Math. Đó là lý do tại sao tôi viết hoa M.
Pratik Deoghare

6
OP đã không hỏi các phương thức tĩnh là gì. Anh ấy hỏi lợi thế của họ là gì. Bạn đang giải thích cách sử dụng chúng chứ không phải chúng hữu ích như thế nào. Trong ví dụ cụ thể của bạn, một không gian tên sẽ có ý nghĩa hơn nhiều.
Javier

4
-1: Lời giải thích hay và đúng, nhưng một ví dụ được chọn rất kém: không có lý do gì để bạn trở thành Mathmột lớp học ngay từ đầu, một học phần sẽ phù hợp hơn. Hãy cố gắng tìm một ví dụ về một lớp có ý nghĩa như một lớp, không phải là một ví dụ "không ai muốn tạo đối tượng của" .
MestreLion

3
sau đó đưa ra một ví dụ về trường hợp sử dụng hợp pháp cho một (hoặc một số ) phương thức của các lớp là tĩnh, nhưng không phải tất cả . Nếu tất cả các phương thức của một lớp là tĩnh, thì lớp phải là một mô-đun. Nếu không có động tĩnh nào, thì bạn không trả lời câu hỏi.
MestreLion

13

Khi bạn gọi một đối tượng hàm từ một cá thể đối tượng, nó sẽ trở thành một 'phương thức liên kết' và lấy chính đối tượng cá thể đó được truyền vào dưới dạng đối số đầu tiên.

Khi bạn gọi một classmethodđối tượng (bao bọc một đối tượng hàm) trên một cá thể đối tượng, lớp của đối tượng cá thể sẽ được chuyển vào dưới dạng đối số đầu tiên.

Khi bạn gọi một staticmethodđối tượng (bao bọc một đối tượng hàm), không có đối số đầu tiên ngầm định nào được sử dụng.

class Foo(object):

    def bar(*args):
        print args

    @classmethod
    def baaz(*args):
        print args

    @staticmethod
    def quux(*args):
        print args

>>> foo = Foo()

>>> Foo.bar(1,2,3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method bar() must be called with Foo instance as first argument (got int instance instead)
>>> Foo.baaz(1,2,3)
(<class 'Foo'>, 1, 2, 3)
>>> Foo.quux(1,2,3)
(1, 2, 3)

>>> foo.bar(1,2,3)
(<Foo object at 0x1004a4510>, 1, 2, 3)
>>> foo.baaz(1,2,3)
(<class 'Foo'>, 1, 2, 3)
>>> foo.quux(1,2,3)
(1, 2, 3)

3
+1: Cuối cùng là lời giải thích tuyệt vời về phương thức tĩnh phương thức lớp là gì. Mặc dù bạn không giải thích lý do tại sao người ta muốn sử dụng một phương thức tĩnh, nhưng ít nhất bạn đã giải thích rõ ràng trong một ví dụ đơn giản rằng cả hai đều tốt hơn nhiều so với các tài liệu chính thức.
MestreLion

3

phương thức tĩnh rất tuyệt vì bạn không phải khai báo một thể hiện của đối tượng mà phương thức đó thuộc về.

Trang web của python có một số tài liệu tuyệt vời về các phương thức tĩnh tại đây:
http://docs.python.org/library/functions.html#staticmethod


Cảm ơn David. Nhưng tại sao sau đó không xác định mọi phương thức là một phương thức tĩnh, vì chúng cũng hoạt động trên các cá thể. Làm như vậy có hạn chế nào không?
Curious2learn

4
@ Curious2learn: Không, với các phương thức tĩnh, bạn không có quyền truy cập vào cá thể: Cá thể bị bỏ qua ngoại trừ lớp của nó.
Felix Kling

4
Đối số đó sẽ đúng trong Java, nơi các hàm không thể tự tồn tại mà luôn được định nghĩa trong ngữ cảnh của một lớp. Nhưng trong Python, bạn có thể có các hàm và các hàm lớp tĩnh. Câu trả lời này không thực sự cho thấy lý do tại sao phải chọn một phương thức tĩnh thay vì một phương thức không có trong một lớp.
extraneon

1
@extraneon - đó là vấn đề nhiều hơn về tùy chọn tổ chức mã; có các phương thức tĩnh cung cấp một tùy chọn bổ sung.
Charles Duffy

@Felix - Cảm ơn. Điều này làm rõ lý do tại sao mọi phương thức không nên là một phương thức tĩnh.
Curious2learn

3

Các phương án để một staticmethodlà: classmethod, instancemethod, và function. Nếu bạn không biết đây là những gì, hãy cuộn xuống phần cuối cùng. Nếu a staticmethodtốt hơn bất kỳ lựa chọn thay thế nào trong số này, tùy thuộc vào mục đích nó được viết.

ưu điểm của phương thức tĩnh Python

  • Nếu bạn không cần quyền truy cập vào các thuộc tính hoặc phương thức của lớp hoặc cá thể, a staticmethodtốt hơn a classmethodhoặc instancemethod. Bằng cách đó, rõ ràng (từ trình @staticmethodtrang trí) rằng trạng thái của lớp và cá thể không được đọc hoặc sửa đổi. Tuy nhiên, việc sử dụng một functionlàm cho sự phân biệt đó trở nên rõ ràng hơn (xem nhược điểm).
  • Chữ ký cuộc gọi của a staticmethodgiống với chữ ký của a classmethodhoặc instancemethod, cụ thể là <instance>.<method>(<arguments>). Do đó, nó có thể dễ dàng được thay thế bằng một trong ba nếu cần sau này hoặc trong một lớp dẫn xuất. Bạn không thể làm điều đó với một đơn giản function.
  • A staticmethodcó thể được sử dụng thay cho a functionđể làm rõ rằng nó thuộc về chủ quan của một lớp và để ngăn chặn xung đột không gian tên.

nhược điểm của phương thức tĩnh Python

  • Nó không thể truy cập các thuộc tính hoặc phương thức của cá thể hoặc lớp.
  • Chữ ký cuộc gọi của a staticmethodgiống với chữ ký của a classmethodhoặc instancemethod. Điều này che giấu thực tế là staticmethodkhông thực sự đọc hoặc sửa đổi bất kỳ thông tin đối tượng nào. Điều này làm cho mã khó đọc hơn. Tại sao không chỉ sử dụng a function?
  • A staticmethodrất khó sử dụng lại nếu bạn cần gọi nó từ bên ngoài lớp / thể hiện nơi nó được định nghĩa. Nếu có bất kỳ tiềm năng để sử dụng lại, a functionlà lựa chọn tốt hơn.
  • staticmethodnày hiếm khi được sử dụng, vì vậy những người đọc mã bao gồm một mã có thể mất nhiều thời gian hơn một chút để đọc.

các lựa chọn thay thế cho một phương thức tĩnh trong Python

Để thảo luận về những lợi thế của staticmethod, chúng ta cần biết các lựa chọn thay thế là gì và chúng khác nhau như thế nào.

  • Các staticmethodthuộc về một lớp học nhưng không thể truy cập hoặc sửa đổi bất kỳ trường hợp hoặc lớp thông tin.

Có ba lựa chọn thay thế cho nó:

  • classmethodquyền truy cập vào lớp của người gọi.
  • instancemethodquyền truy cập vào cá thể của người gọi và lớp của nó.
  • Không functioncó gì để làm với các lớp học. Nó là khả năng gần nhất với staticmethod.

Đây là những gì điều này trông giống như trong mã:

# function
# has nothing to do with a class
def make_cat_noise(asker_name):
    print('Hi %s, mieets mieets!' % asker_name)

# Yey, we can make cat noises before we've even defined what a cat is!
make_cat_noise('JOey')  # just a function

class Cat:
    number_of_legs = 4

    # special instance method __init__
    def __init__(self, name):
        self.name = name

    # instancemethod
    # the instance (e.g. Cat('Kitty')) is passed as the first method argument
    def tell_me_about_this_animal(self, asker_name):
        print('Hi %s, This cat has %d legs and is called %s'
              % (asker_name, self.number_of_legs, self.name))

    # classmethod
    # the class (e.g. Cat) is passed as the first method argument
    # by convention we call that argument cls
    @classmethod
    def tell_me_about_cats(cls, asker_name):
        print("Hi %s, cats have %d legs."
              % (asker_name, cls.number_of_legs))
        # cls.name  # AttributeError because only the instance has .name
        # self.name  # NameError because self isn't defined in this namespace

    # staticmethod
    # no information about the class or the instance is passed to the method
    @staticmethod
    def make_noise(asker_name):
        print('Hi %s, meooow!' % asker_name)
        # class and instance are not accessible from here

# one more time for fun!
make_cat_noise('JOey')  # just a function

# We just need the class to call a classmethod or staticmethod:
Cat.make_noise('JOey')  # staticmethod
Cat.tell_me_about_cats('JOey')  # classmethod
# Cat.tell_me_about_this_animal('JOey')  # instancemethod -> TypeError

# With an instance we can use instancemethod, classmethod or staticmethod
mycat = Cat('Kitty')  # mycat is an instance of the class Cat
mycat.make_noise('JOey')  # staticmethod
mycat.tell_me_about_cats('JOey')  # classmethod
mycat.tell_me_about_this_animal('JOey')  # instancemethod

1

Bởi vì các chức năng không gian tên rất hay (như đã chỉ ra trước đây):

  1. Khi tôi muốn rõ ràng về các phương thức không thay đổi trạng thái của đối tượng, tôi sử dụng các phương thức tĩnh. Điều này không khuyến khích mọi người trong nhóm của tôi bắt đầu thay đổi các thuộc tính của đối tượng trong các phương thức đó.

  2. Khi tôi cấu trúc lại mã thực sự bị hỏng, tôi bắt đầu bằng cách cố gắng tạo càng nhiều phương pháp @staticmethodcàng tốt. Điều này cho phép tôi sau đó trích xuất các phương thức này vào một lớp - mặc dù tôi đồng ý, đây là điều hiếm khi tôi sử dụng, nó đã có ích một vài lần.


1

Theo ước tính của tôi, không có lợi ích hiệu suất duy nhất của việc sử dụng @staticmethods so với việc chỉ xác định hàm bên ngoài và tách biệt với lớp mà nó sẽ là một @staticmethodcủa.

Điều duy nhất tôi muốn nói biện minh cho sự tồn tại của họ là sự tiện lợi. Các phương thức tĩnh phổ biến trong các ngôn ngữ lập trình phổ biến khác, vậy tại sao không phải là python? Nếu bạn muốn tạo một hàm có hành vi được liên kết rất chặt chẽ với lớp mà bạn đang tạo nó nhưng nó không thực sự truy cập / sửa đổi dữ liệu nội bộ của một thể hiện của lớp theo cách biện minh cho việc khái niệm hóa nó như một điển hình phương pháp của lớp đó sau đó tát một@staticmethod lên trên nó và bất kỳ ai đọc mã của bạn sẽ ngay lập tức học được nhiều điều về bản chất của phương thức và mối quan hệ của nó với lớp.

Một điều tôi thỉnh thoảng muốn làm là đặt chức năng mà lớp của tôi sử dụng nội bộ rất nhiều vào các tệp riêng tư @staticmethod. Bằng cách đó, tôi không làm lộn xộn API mà mô-đun của tôi hiển thị với các phương pháp mà không ai sử dụng mô-đun của tôi cần phải xem chứ đừng nói sử dụng.


0

Các phương thức tĩnh hầu như không có lý do để tồn tại trong Python. Bạn sử dụng phương thức thể hiện hoặc phương thức lớp.

def method(self, args):
    self.member = something

@classmethod
def method(cls, args):
    cls.member = something

@staticmethod
def method(args):
    MyClass.member = something
    # The above isn't really working
    # if you have a subclass

Bạn đã nói "gần như". Có nơi nào mà chúng có thể tốt hơn các lựa chọn thay thế không?
Javier

@Javier: Tôi không thể nghĩ ra một cái, nhưng có lẽ là một cái, tại sao phương thức đó lại được đưa vào thư viện Python?
Georg Schölly

1
@Javier, @Georg: Bạn có niềm tin tuyệt đối rằng kho ngữ liệu Python không có phần thô.
Charles Merriam

-1: Câu trả lời này không trình bày bất kỳ trường hợp sử dụng nàostaticmethod hoặc bất kỳ giải thích nào về việc a classmethodlà gì hoặc tại sao và nó "tốt hơn" so với các câu trả lời tĩnh.
MestreLion

@CharlesMerriam: Tất nhiên các phương thức tĩnh có cách sử dụng của chúng, nếu không nó sẽ bị loại bỏ hoặc không được dùng nữa trong Python 3 (nơi hầu hết các cruft cũ đã bị loại bỏ). Không có một single từ chống lại staticmethodtrong tài liệu để hỗ trợ tuyên bố của bạn rằng đó là cruft, hoặc khiếu nại Georg rằng classmethodnên được sử dụng thay).
MestreLion
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.