Các phương thức tĩnh trong Python?


1719

Có thể có các phương thức tĩnh trong Python mà tôi có thể gọi mà không cần khởi tạo một lớp, như:

ClassName.static_method()

Câu trả lời:


2026

Đúng, sử dụng trang trí tĩnh

class MyClass(object):
    @staticmethod
    def the_static_method(x):
        print(x)

MyClass.the_static_method(2)  # outputs 2

Lưu ý rằng một số mã có thể sử dụng phương thức cũ để xác định phương thức tĩnh, sử dụng staticmethodnhư một hàm thay vì trang trí. Điều này chỉ nên được sử dụng nếu bạn phải hỗ trợ các phiên bản Python cổ (2.2 và 2.3)

class MyClass(object):
    def the_static_method(x):
        print(x)
    the_static_method = staticmethod(the_static_method)

MyClass.the_static_method(2)  # outputs 2

Điều này hoàn toàn giống với ví dụ đầu tiên (sử dụng @staticmethod), chỉ là không sử dụng cú pháp trang trí đẹp

Cuối cùng, sử dụng một staticmethod()cách tiết kiệm! Có rất ít tình huống trong đó các phương thức tĩnh là cần thiết trong Python và tôi đã thấy chúng được sử dụng nhiều lần trong đó một hàm "cấp cao nhất" riêng biệt sẽ rõ ràng hơn.


Sau đây là nguyên văn từ tài liệu ::

Một phương thức tĩnh không nhận được một đối số đầu tiên ẩn. Để khai báo một phương thức tĩnh, sử dụng thành ngữ này:

class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

Biểu mẫu @staticmethod là một trình trang trí hàm - xem mô tả về các định nghĩa hàm trong các định nghĩa hàm để biết chi tiết.

Nó có thể được gọi hoặc trên lớp (chẳng hạn C.f()) hoặc trên một thể hiện (chẳng hạn như C().f()). Ví dụ được bỏ qua ngoại trừ lớp của nó.

Các phương thức tĩnh trong Python tương tự như các phương thức được tìm thấy trong Java hoặc C ++. Đối với một khái niệm cao cấp hơn, xem classmethod().

Để biết thêm thông tin về các phương thức tĩnh, hãy tham khảo tài liệu về phân cấp loại tiêu chuẩn trong Phân cấp loại tiêu chuẩn .

Mới trong phiên bản 2.2.

Thay đổi trong phiên bản 2.4: Đã thêm cú pháp trang trí chức năng.


15
Tại sao thêm @staticmethodtrang trí hoặc sử dụng một con trỏ hàm, khi bạn chỉ cần tránh selftham số đầu tiên ? Chà, đối với một đối tượng a, bạn sẽ không thể gọi a.your_static_method(), được phép sử dụng trong các ngôn ngữ khác, nhưng dù sao nó cũng bị coi là một thực tiễn xấu và trình biên dịch luôn cảnh báo về điều đó
SomethingS Something

1
Tôi đang sử dụng python 2.7.12, với trang trí @staticmethod tôi không thể gọi phương thức tĩnh đầu tiên được xác định. Lỗi givin của nó : LoạiError: đối tượng 'staticmethod' không thể gọi được
Supratim Samantray

Supratim Samantray, bạn có chắc không? bởi vì ở đây nó đã tuyên bố rõ ràng rằng nó có thể được sử dụng sau 2.4.
realslimshanky

11
@S SomethingS Something, nếu bạn không sử dụng tên biến "self", nhưng không thêm trình trang trí, đối số đầu tiên, ví dụ arg1 vẫn sẽ là tham chiếu đến cá thể tự. Tên tự chỉ là một quy ước.
George Moutsopoulos

1
@GeorgeMoutsopoulos - đồng ý chung. Nhưng - dù sao bạn cũng sẽ có thể gọi phương thức ClassName.methodName()như thể nó là phương thức tĩnh, và sau đó selfsẽ không được cung cấp cho phương thức. Như bạn đã nói, vẫn có thể gọi phương thức này là ClassInstance.methodName()selfsẽ được cung cấp làm tham số đầu tiên, bất kể tên của nó là gì.
Một cái gì đó

220

Tôi nghĩ rằng Steven thực sự đúng . Sau đó, để trả lời câu hỏi ban đầu, để thiết lập một phương thức lớp, chỉ cần giả sử rằng đối số đầu tiên sẽ không phải là một cá thể gọi, và sau đó đảm bảo rằng bạn chỉ gọi phương thức từ lớp.

(Lưu ý rằng câu trả lời này đề cập đến Python 3.x. Trong Python 2.x bạn sẽ nhận được một cách TypeErrorgọi phương thức trên chính lớp đó.)

Ví dụ:

class Dog:
    count = 0 # this is a class variable
    dogs = [] # this is a class variable

    def __init__(self, name):
        self.name = name #self.name is an instance variable
        Dog.count += 1
        Dog.dogs.append(name)

    def bark(self, n): # this is an instance method
        print("{} says: {}".format(self.name, "woof! " * n))

    def rollCall(n): #this is implicitly a class method (see comments below)
        print("There are {} dogs.".format(Dog.count))
        if n >= len(Dog.dogs) or n < 0:
            print("They are:")
            for dog in Dog.dogs:
                print("  {}".format(dog))
        else:
            print("The dog indexed at {} is {}.".format(n, Dog.dogs[n]))

fido = Dog("Fido")
fido.bark(3)
Dog.rollCall(-1)
rex = Dog("Rex")
Dog.rollCall(0)

Trong mã này, phương thức "rollCall" giả định rằng đối số đầu tiên không phải là một thể hiện (vì nó sẽ được gọi bởi một thể hiện thay vì một lớp). Miễn là "rollCall" được gọi từ lớp chứ không phải là một thể hiện, mã sẽ hoạt động tốt. Nếu chúng ta cố gắng gọi "rollCall" từ một ví dụ, ví dụ:

rex.rollCall(-1)

tuy nhiên, nó sẽ khiến một ngoại lệ được đưa ra bởi vì nó sẽ gửi hai đối số: chính nó và -1 và "rollCall" chỉ được xác định để chấp nhận một đối số.

Ngẫu nhiên, rex.rollCall () sẽ gửi đúng số lượng đối số, nhưng cũng sẽ gây ra ngoại lệ vì bây giờ n sẽ đại diện cho một thể hiện Dog (tức là rex) khi hàm dự kiến ​​n là số.

Đây là nơi trang trí xuất hiện: Nếu chúng ta đi trước phương thức "rollCall" với

@staticmethod

sau đó, bằng cách nói rõ rằng phương thức này là tĩnh, chúng ta thậm chí có thể gọi nó từ một thể hiện. Hiện nay,

rex.rollCall(-1)

sẽ làm việc Việc chèn @staticmethod trước một định nghĩa phương thức, sau đó, ngăn một cá thể gửi chính nó làm đối số.

Bạn có thể xác minh điều này bằng cách thử đoạn mã sau có và không có dòng @staticmethod nhận xét.

class Dog:
    count = 0 # this is a class variable
    dogs = [] # this is a class variable

    def __init__(self, name):
        self.name = name #self.name is an instance variable
        Dog.count += 1
        Dog.dogs.append(name)

    def bark(self, n): # this is an instance method
        print("{} says: {}".format(self.name, "woof! " * n))

    @staticmethod
    def rollCall(n):
        print("There are {} dogs.".format(Dog.count))
        if n >= len(Dog.dogs) or n < 0:
            print("They are:")
            for dog in Dog.dogs:
                print("  {}".format(dog))
        else:
            print("The dog indexed at {} is {}.".format(n, Dog.dogs[n]))


fido = Dog("Fido")
fido.bark(3)
Dog.rollCall(-1)
rex = Dog("Rex")
Dog.rollCall(0)
rex.rollCall(-1)

8
Ví dụ đầu tiên (gọi theo lớp mà không có @staticmethod) không hoạt động với tôi trên Python 2.7. Tôi nhận được "TypeError: phương thức không liên kết rollCall () phải được gọi với đối tượng Dog là đối số đầu tiên (có thể hiện int thay thế)"
tba

2
Điều này không hoạt động. Cả Dog.rollCall (-1) và rex.rollCall (-1) đều trả lại như nhauTypeError: unbound method rollCall() must be called with Dog instance as first argument (got int instance instead)
Găng tay

3
@MestreLion Tôi nghĩ rằng đây không phải là một lợi ích to lớn, vì tất cả những gì nó làm là làm rối loạn các phương thức tĩnh và cá thể và làm cho cái này trông giống cái kia. Nếu bạn biết một phương thức là tĩnh, bạn nên truy cập nó như một phương thức tĩnh. Thực tế là phương thức không cần hoặc sử dụng thể hiện của bạn nên hiển nhiên ở bất cứ nơi nào bạn sử dụng phương thức. Tôi luôn luôn sử dụng T.my_static_method()hoặc type(my_t_instance).my_static_method(), vì điều này rõ ràng hơn và rõ ràng rằng tôi đang gọi một phương thức tĩnh.
Asad Saeeduddin

81

Có, hãy kiểm tra trang trí tĩnh :

>>> class C:
...     @staticmethod
...     def hello():
...             print "Hello World"
...
>>> C.hello()
Hello World

54

Bạn không thực sự cần phải sử dụng @staticmethodtrang trí. Chỉ cần khai báo một phương thức (không mong đợi tham số tự) và gọi nó từ lớp. Trình trang trí chỉ ở đó trong trường hợp bạn muốn có thể gọi nó từ một ví dụ (đó không phải là điều bạn muốn làm)

Hầu hết, bạn chỉ sử dụng các chức năng mặc dù ...


Điều này sẽ không hoạt động với python 2.5.2 class Dummy: def static1(): print "hello from static1" @staticmethod def static2(): print "hello from static2" Dummy.static2() Dummy.static1() Kết quả: xin chào từ static2 TracBack <cuộc gọi gần đây nhất>: Tệp "ll.py", dòng 46, trong <module> Dummy.static1 () TypeError: phương thức không gắn kết static1 () phải là được gọi với ví dụ Dummy là đối số đầu tiên (không có gì thay thế)
Mahori

7
Điều này không hoạt động trong một lớp học. Python sẽ vượt qua selfnhư là đối số đầu tiên trừ khi bạn nói không. (xem: trang trí)
Navin

9
Trên thực tế, điều này là sai trong 2.7 và hợp pháp kể từ 3.X (đã được kiểm tra tiền phạt trong 3.2). Đó là bạn không thể gọi một phương thức từ ngữ cảnh của một lớp mà không có trình trang trí @staticmethod trong 2.7 trở xuống. Trong 3.2, nó hoạt động và sẽ chèn một selfref thích hợp tùy thuộc vào cách gọi của nó. Trường hợp thử nghiệm: pastebin.com/12DDV7DB .
spinkus

2
Kỹ thuật chính xác, nhưng một giải pháp khủng khiếp. Trình staticmethodtrang trí cho phép một người gọi hàm trên cả lớp và một thể hiện (giải pháp này không thành công khi gọi hàm trên một thể hiện).
Ethan Furman

một phương thức có thể được gọi như bất kỳ thuộc tính nào khác của một đối tượng bằng cách sử dụng ký hiệu dấu chấm. class C: def callme(): print('called'); C.callme()
pouya

32

Các phương thức tĩnh trong Python?

Có thể có các phương thức tĩnh trong Python để tôi có thể gọi chúng mà không cần khởi tạo một lớp, như:

ClassName.StaticMethod()

Có, các phương thức tĩnh có thể được tạo như thế này (mặc dù Pythonic sử dụng dấu gạch dưới thay vì CamelCase nhiều hơn một chút ):

class ClassName(object):

    @staticmethod
    def static_method(kwarg1=None):
        '''return a value that is a function of kwarg1'''

Ở trên sử dụng cú pháp trang trí. Cú pháp này tương đương với

class ClassName(object):

    def static_method(kwarg1=None):
        '''return a value that is a function of kwarg1'''

    static_method = staticmethod(static_method)

Điều này có thể được sử dụng như bạn mô tả:

ClassName.static_method()

Một ví dụ dựng sẵn của một phương thức tĩnh là str.maketrans()trong Python 3, là một hàm trong stringmô-đun trong Python 2.


Một tùy chọn khác có thể được sử dụng như bạn mô tả là classmethod, sự khác biệt là classmethod lấy lớp làm đối số đầu tiên ẩn và nếu được phân lớp, thì nó sẽ nhận lớp con làm đối số đầu tiên ẩn.

class ClassName(object):

    @classmethod
    def class_method(cls, kwarg1=None):
        '''return a value that is a function of the class and kwarg1'''

Lưu ý rằng đó clskhông phải là một tên bắt buộc cho đối số đầu tiên, nhưng hầu hết các lập trình viên Python có kinh nghiệm sẽ xem xét nó được thực hiện tồi nếu bạn sử dụng bất cứ điều gì khác.

Chúng thường được sử dụng như là các nhà xây dựng thay thế.

new_instance = ClassName.class_method()

Một ví dụ dựng sẵn là dict.fromkeys():

new_dict = dict.fromkeys(['key1', 'key2'])

11

Ngoài các đặc thù về cách các đối tượng phương thức tĩnh hoạt động, có một loại vẻ đẹp nhất định mà bạn có thể tấn công với chúng khi nói đến việc tổ chức mã cấp mô-đun của bạn.

# garden.py
def trim(a):
    pass

def strip(a):
    pass

def bunch(a, b):
    pass

def _foo(foo):
    pass

class powertools(object):
    """
    Provides much regarded gardening power tools.
    """
    @staticmethod
    def answer_to_the_ultimate_question_of_life_the_universe_and_everything():
        return 42

    @staticmethod
    def random():
        return 13

    @staticmethod
    def promise():
        return True

def _bar(baz, quux):
    pass

class _Dice(object):
    pass

class _6d(_Dice):
    pass

class _12d(_Dice):
    pass

class _Smarter:
    pass

class _MagicalPonies:
    pass

class _Samurai:
    pass

class Foo(_6d, _Samurai):
    pass

class Bar(_12d, _Smarter, _MagicalPonies):
    pass

...

# tests.py
import unittest
import garden

class GardenTests(unittest.TestCase):
    pass

class PowertoolsTests(unittest.TestCase):
    pass

class FooTests(unittest.TestCase):
    pass

class BarTests(unittest.TestCase):
    pass

...

# interactive.py
from garden import trim, bunch, Foo

f = trim(Foo())
bunch(f, Foo())

...

# my_garden.py
import garden
from garden import powertools

class _Cowboy(garden._Samurai):
    def hit():
        return powertools.promise() and powertools.random() or 0

class Foo(_Cowboy, garden.Foo):
    pass

Bây giờ nó trở nên trực quan hơn và tự ghi lại tài liệu trong đó bối cảnh một số thành phần được sử dụng và nó giải thích lý tưởng cho việc đặt tên các trường hợp thử nghiệm riêng biệt cũng như có cách tiếp cận đơn giản về cách các mô-đun thử nghiệm ánh xạ tới các mô-đun thực tế trong các thử nghiệm cho người theo chủ nghĩa thuần túy .

Tôi thường thấy khả thi khi áp dụng phương pháp này để tổ chức mã tiện ích của dự án. Rất thường xuyên, mọi người ngay lập tức vội vàng và tạo ra một utilsgói và kết thúc với 9 mô-đun trong đó một mô-đun có 120 LỘC và phần còn lại là hai chục LỘC tốt nhất. Tôi thích bắt đầu với điều này và chuyển đổi nó thành một gói và chỉ tạo các mô-đun cho những con thú thực sự xứng đáng với chúng:

# utils.py
class socket(object):
    @staticmethod
    def check_if_port_available(port):
        pass

    @staticmethod
    def get_free_port(port)
        pass

class image(object):
    @staticmethod
    def to_rgb(image):
        pass

    @staticmethod
    def to_cmyk(image):
        pass

10

Có lẽ tùy chọn đơn giản nhất là đặt các hàm đó bên ngoài lớp:

class Dog(object):
    def __init__(self, name):
        self.name = name

    def bark(self):
        if self.name == "Doggy":
            return barking_sound()
        else:
            return "yip yip"

def barking_sound():
    return "woof woof"

Sử dụng phương thức này, các hàm sửa đổi hoặc sử dụng trạng thái đối tượng bên trong (có tác dụng phụ) có thể được giữ trong lớp và các hàm tiện ích có thể sử dụng lại có thể được chuyển ra bên ngoài.

Hãy nói rằng tập tin này được gọi dogs.py. Để sử dụng chúng, bạn sẽ gọi dogs.barking_sound()thay vì dogs.Dog.barking_sound.

Nếu bạn thực sự cần một phương thức tĩnh là một phần của lớp, bạn có thể sử dụng trình trang trí tĩnh .


1

Vì vậy, các phương thức tĩnh là các phương thức có thể được gọi mà không tạo đối tượng của một lớp. Ví dụ :-

    @staticmethod
    def add(a, b):
        return a + b

b = A.add(12,12)
print b

Trong ví dụ trên, phương thức addđược gọi bởi tên lớp Achứ không phải tên đối tượng.


-3

Các phương thức tĩnh của Python có thể được tạo theo hai cách.

  1. Sử dụng tĩnh điện ()

    class Arithmetic:
        def add(x, y):
            return x + y
    # create add static method
    Arithmetic.add = staticmethod(Arithmetic.add)
    
    print('Result:', Arithmetic.add(15, 10))

Đầu ra:

Kết quả: 25

  1. Sử dụng @staticmethod

    class Arithmetic:
    
    # create add static method
    @staticmethod
    def add(x, y):
        return x + y
    
    print('Result:', Arithmetic.add(15, 10))

Đầu ra:

Kết quả: 25


Bạn chỉ cần cung cấp các ví dụ và chia sẻ những cách như thế nào bạn có thể làm điều đó. Bạn đã không giải thích ý nghĩa của các phương pháp.
zixuan

-4

Tôi gặp câu hỏi này theo thời gian. Trường hợp sử dụng và ví dụ mà tôi thích là:

jeffs@jeffs-desktop:/home/jeffs  $ python36
Python 3.6.1 (default, Sep  7 2017, 16:36:03) 
[GCC 6.3.0 20170406] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cmath
>>> print(cmath.sqrt(-4))
2j
>>>
>>> dir(cmath)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atanh', 'cos', 'cosh', 'e', 'exp', 'inf', 'infj', 'isclose', 'isfinite', 'isinf', 'isnan', 'log', 'log10', 'nan', 'nanj', 'phase', 'pi', 'polar', 'rect', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau']
>>> 

Không có nghĩa gì khi tạo một đối tượng của lớp cmath, bởi vì không có trạng thái trong một đối tượng cmath. Tuy nhiên, cmath là một tập hợp các phương thức có liên quan theo một cách nào đó. Trong ví dụ của tôi ở trên, tất cả các hàm trong cmath hoạt động trên các số phức theo một cách nào đó.


Bạn đã không trả lời câu hỏi nhưng thay vào đó bạn đã cung cấp một ví dụ.
zixuan
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.