วิธีการคงที่ในงูหลาม?


1719

เป็นไปได้ไหมที่จะมีเมธอดสแตติกใน Python ที่ฉันสามารถเรียกได้โดยไม่ต้องกำหนดค่าเริ่มต้นของคลาสเช่น:

ClassName.static_method()

คำตอบ:


2026

อ๋อใช้StaticMethodมัณฑนากร

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

MyClass.the_static_method(2)  # outputs 2

โปรดทราบว่าบางรหัสอาจใช้วิธีการเก่าของการกำหนดวิธีการคงที่โดยใช้staticmethodเป็นฟังก์ชั่นมากกว่ามัณฑนากร ควรใช้เฉพาะเมื่อคุณต้องสนับสนุน Python เวอร์ชันโบราณ (2.2 และ 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

นี่เหมือนกับตัวอย่างแรกทั้งหมด (โดยใช้@staticmethod) เพียงแค่ไม่ใช้ไวยากรณ์ของมัณฑนากรที่ดี

ในที่สุดใช้staticmethod()เท่าที่จำเป็น! มีสถานการณ์น้อยมากที่จำเป็นต้องใช้วิธีการคงที่ใน Python และฉันเคยเห็นพวกเขาใช้หลายครั้งซึ่งฟังก์ชั่น "ระดับบนสุด" แยกต่างหากจะชัดเจนขึ้น


ต่อไปนี้เป็นคำต่อคำจากเอกสาร :

วิธีการคงที่ไม่ได้รับการโต้แย้งครั้งแรกโดยปริยาย ในการประกาศเมธอดสแตติกให้ใช้สำนวนนี้:

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

รูปแบบ @staticmethod เป็นตัวตกแต่งฟังก์ชั่น- ดูคำอธิบายของคำจำกัดความของฟังก์ชั่นในคำจำกัดความของฟังก์ชั่นสำหรับรายละเอียด

มันสามารถเรียกได้ทั้งในชั้นเรียน (เช่นC.f()) หรือในอินสแตนซ์ (เช่นC().f()) อินสแตนซ์ถูกละเว้นยกเว้นคลาสของมัน

วิธีการคงที่ใน Python คล้ายกับที่พบใน Java หรือ C ++ classmethod()สำหรับแนวความคิดขั้นสูงเพิ่มเติมโปรดดูที่

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการคงปรึกษาเอกสารเกี่ยวกับลำดับชั้นมาตรฐานชนิดในลำดับชั้นมาตรฐานชนิด

ใหม่ในเวอร์ชัน 2.2

เปลี่ยนเป็นเวอร์ชั่น 2.4: เพิ่มฟังก์ชั่นไวยากรณ์มัณฑนากร


15
ทำไมต้องเพิ่มการ@staticmethodตกแต่งหรือใช้ตัวชี้ฟังก์ชั่นเมื่อคุณสามารถหลีกเลี่ยงselfพารามิเตอร์แรกได้ สำหรับวัตถุaคุณจะไม่สามารถโทรออกa.your_static_method()ได้ซึ่งได้รับอนุญาตในภาษาอื่น ๆ แต่ก็ถือว่าเป็นการปฏิบัติที่ไม่ดีและคอมไพเลอร์เตือนเสมอเกี่ยวกับมัน
SomethingSomething

1
ฉันใช้ python 2.7.12 กับมัณฑนากร @staticmethod ฉันไม่สามารถเรียกวิธีคงที่แรกที่กำหนด ข้อผิดพลาด givin: TypeError: วัตถุ 'staticmethod' ไม่สามารถเรียกได้
Supratim Samantray

Supratim Samantray คุณแน่ใจเหรอ เนื่องจากที่นี่มีการระบุไว้อย่างชัดเจนว่าสามารถใช้งานได้หลังจาก 2.4
realslimshanky

11
@SomethingSomething ถ้าคุณไม่ใช้ชื่อตัวแปร "self" แต่ไม่ต้องเพิ่ม decorator อาร์กิวเมนต์แรกเช่น arg1 จะยังคงเป็นการอ้างอิงถึงอินสแตนซ์ของตัวเอง ชื่อตัวเองเป็นเพียงการประชุม
George Moutsopoulos

1
@GeorgeMoutsopoulos - ตกลงกันโดยทั่วไป แต่ - คุณจะสามารถเรียกใช้เมธอดราวกับClassName.methodName()ว่ามันเป็นสแตติกและจากนั้นselfจะไม่มีการจัดเตรียมเมธอด ดังที่คุณกล่าวว่าจะยังสามารถเรียกวิธีนี้ได้เช่นClassInstance.methodName()กันและselfจะให้เป็นพารามิเตอร์แรกโดยไม่คำนึงถึงชื่อ
SomethingSomething

220

ผมคิดว่าสตีเว่นเป็นจริงที่ถูกต้อง ในการตอบคำถามเดิมจากนั้นในการตั้งค่าวิธีการเรียนเพียงแค่สมมติว่าอาร์กิวเมนต์แรกไม่ได้เป็นอินสแตนซ์การโทรและจากนั้นตรวจสอบให้แน่ใจว่าคุณเรียกวิธีการจากชั้นเรียนเท่านั้น

(โปรดทราบว่าคำตอบนี้หมายถึง Python 3.x ใน Python 2.x คุณจะได้รับTypeErrorสำหรับการเรียกใช้เมธอดในคลาสเอง)

ตัวอย่างเช่น:

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)

ในรหัสนี้เมธอด "rollCall" สันนิษฐานว่าอาร์กิวเมนต์แรกไม่ใช่อินสแตนซ์ (อย่างที่มันจะเป็นถ้ามันถูกเรียกโดยอินสแตนซ์แทนที่จะเป็นคลาส) ตราบใดที่มีการเรียก "rollCall" จากคลาสแทนที่จะเป็นอินสแตนซ์รหัสจะทำงานได้ดี หากเราพยายามโทรหา "rollCall" จากตัวอย่างเช่น:

rex.rollCall(-1)

อย่างไรก็ตามมันจะทำให้เกิดข้อยกเว้นที่จะยกขึ้นเพราะมันจะส่งสองข้อโต้แย้ง: ตัวเองและ -1 และ "rollCall" ถูกกำหนดให้ยอมรับเพียงหนึ่งอาร์กิวเมนต์

อนึ่ง rex.rollCall () จะส่งจำนวนอาร์กิวเมนต์ที่ถูกต้อง แต่ก็จะทำให้เกิดข้อยกเว้นที่จะยกขึ้นเพราะตอนนี้ n จะเป็นตัวแทนของอินสแตนซ์ Dog (เช่น rex) เมื่อฟังก์ชั่นคาดว่าจะเป็นตัวเลข

นี่คือที่มาของการตกแต่ง: หากเรานำหน้าวิธี "rollCall" ด้วย

@staticmethod

จากนั้นโดยการระบุอย่างชัดเจนว่าวิธีการเป็นแบบคงที่เราสามารถเรียกมันได้จากอินสแตนซ์ ตอนนี้

rex.rollCall(-1)

จะทำงาน. การแทรก @staticmethod ก่อนคำนิยามวิธีการจากนั้นหยุดอินสแตนซ์จากการส่งตัวเองเป็นข้อโต้แย้ง

คุณสามารถตรวจสอบสิ่งนี้ได้โดยลองใช้รหัสต่อไปนี้โดยใช้และไม่มีบรรทัด @staticmethod ที่ใส่ความคิดเห็นไว้

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
ตัวอย่างแรก (การเรียกตามคลาสที่ไม่มี @staticmethod) ไม่ทำงานสำหรับฉันใน Python 2.7 ฉันได้รับ "TypeError: unbound method rollCall () ต้องเรียกด้วย Dog อินสแตนซ์เป็นอาร์กิวเมนต์แรก (มีอินสแตนซ์อินแทน)"
tba

2
สิ่งนี้ใช้ไม่ได้ ทั้ง Dog.rollCall (-1) และ rex.rollCall (-1) กลับมาเหมือนเดิมTypeError: unbound method rollCall() must be called with Dog instance as first argument (got int instance instead)
Mittenchops

3
@ MestreLion ฉันคิดว่านี่ไม่ได้เป็นประโยชน์อย่างมากเนื่องจากทั้งหมดจะเป็นการยุ่งเหยิงวิธีแบบคงที่และอินสแตนซ์และทำให้หนึ่งมีลักษณะเหมือนกัน หากคุณรู้ว่าวิธีใดเป็นแบบคงที่คุณควรเข้าถึงเป็นวิธีแบบคงที่ ความจริงที่ว่าวิธีการไม่ต้องการหรือใช้อินสแตนซ์ของคุณควรจะปรากฏชัดในตัวเองไม่ว่าคุณจะใช้วิธีการใด ฉันมักจะใช้T.my_static_method()หรือtype(my_t_instance).my_static_method()เนื่องจากเป็นที่ชัดเจนและทำให้ชัดเจนทันทีว่าฉันเรียกวิธีคงที่
ซาด Saeeduddin


54

คุณไม่จำเป็นต้องใช้@staticmethodมัณฑนากร เพียงแค่ประกาศเมธอด (ที่ไม่ได้คาดหวังว่าพารามิเตอร์ของตนเอง) และเรียกมันจากชั้นเรียน มัณฑนากรมีเฉพาะในกรณีที่คุณต้องการโทรจากอินสแตนซ์เช่นกัน (ซึ่งไม่ใช่สิ่งที่คุณต้องการจะทำ)

ส่วนใหญ่คุณเพียงแค่ใช้ฟังก์ชั่น แต่ ...


สิ่งนี้จะใช้ไม่ได้กับ python 2.5.2 class Dummy: def static1(): print "hello from static1" @staticmethod def static2(): print "hello from static2" Dummy.static2() Dummy.static1() เอาต์พุต: สวัสดีจาก static2 การติดตามย้อนกลับ <การโทรล่าสุดครั้งล่าสุด>: ไฟล์ "ll.py", บรรทัดที่ 46 ใน <module> Dummy.static1 () TypeError: วิธีที่ไม่ถูกต้อง static1 () เรียกว่ามีอินสแตนซ์ Dummy เป็นอาร์กิวเมนต์แรก (มีอะไรแทน)
วงมโหรี

7
สิ่งนี้ไม่ทำงานในชั้นเรียน Python จะส่งผ่านselfเป็นอาร์กิวเมนต์แรกเว้นแต่ว่าคุณจะไม่บอก (ดู: มัณฑนากร)
Navin

9
ที่จริงแล้วสิ่งนี้ผิดใน 2.7 และถูกกฎหมาย ณ 3.X (ทดสอบค่าปรับใน 3.2) นั่นคือคุณไม่สามารถเรียกเมธอดจากบริบทของคลาสโดยไม่มีมัณฑนากร @staticmethod ใน 2.7 และต่ำกว่า ใน 3.2 มันใช้งานได้และจะแทรกการselfอ้างอิงอย่างเหมาะสมขึ้นอยู่กับว่ามันถูกเรียกว่าอย่างไร กรณีการทดสอบ: pastebin.com/12DDV7DB
spinkus

2
ความถูกต้องทางเทคนิค แต่เป็นทางออกที่น่ากลัว staticmethodมัณฑนากรช่วยให้หนึ่งเพื่อเรียกใช้ฟังก์ชันทั้งในชั้นเรียนและอินสแตนซ์ (แก้ปัญหานี้ล้มเหลวเมื่อมีการเรียกฟังก์ชั่นอินสแตนซ์)
Ethan Furman

วิธีการที่สามารถเรียกว่าเช่นคุณลักษณะอื่น ๆ ของวัตถุโดยใช้เครื่องหมายจุด class C: def callme(): print('called'); C.callme()
pouya

32

วิธีการคงที่ในงูหลาม?

เป็นไปได้หรือไม่ที่จะมีวิธีการคงที่ใน Python เพื่อให้ฉันสามารถเรียกพวกเขาโดยไม่ต้องเริ่มต้นชั้นเรียนเช่น:

ClassName.StaticMethod()

ใช่วิธีการคงที่สามารถสร้างเช่นนี้ (แม้ว่าจะเป็นPythonicเล็กน้อยที่จะใช้ขีดเส้นใต้แทน CamelCase สำหรับวิธีการ):

class ClassName(object):

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

ข้างต้นใช้ไวยากรณ์มัณฑนากร ไวยากรณ์นี้เทียบเท่ากับ

class ClassName(object):

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

    static_method = staticmethod(static_method)

สามารถใช้งานได้ตามที่อธิบายไว้:

ClassName.static_method()

ตัวอย่าง builtin ของวิธีการคงที่อยู่str.maketrans()ใน Python 3 ซึ่งเป็นฟังก์ชั่นในstringโมดูลใน Python 2


ตัวเลือกอื่นที่สามารถใช้ตามที่คุณอธิบายได้คือความclassmethodแตกต่างคือเมธอด class รับคลาสเป็นอาร์กิวเมนต์แรกโดยปริยายและถ้าคลาสย่อยแล้วมันจะได้รับคลาสย่อยเป็นอาร์กิวเมนต์แรกโดยปริยาย

class ClassName(object):

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

โปรดทราบว่าclsไม่ใช่ชื่อที่จำเป็นสำหรับอาร์กิวเมนต์แรก แต่ตัวแปลงสัญญาณ Python ที่มีประสบการณ์ส่วนใหญ่จะพิจารณาว่าทำได้ไม่ดีหากคุณใช้สิ่งอื่น

โดยทั่วไปจะใช้สิ่งเหล่านี้เป็นตัวสร้างทางเลือก

new_instance = ClassName.class_method()

ตัวอย่าง builtin คือdict.fromkeys():

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

11

นอกเหนือจากลักษณะเฉพาะของวิธีการที่วัตถุวิธีการทำงานแบบคงที่มีความงามบางชนิดที่คุณสามารถใช้กับพวกเขาเมื่อมันมาถึงการจัดระเบียบรหัสระดับโมดูลของคุณ

# 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

ตอนนี้กลายเป็นบิตง่ายขึ้นและการจัดทำเอกสารด้วยตนเองในบริบทที่องค์ประกอบบางอย่างมีความหมายที่จะใช้และมันออกมาอย่างยอดเยี่ยมสำหรับการตั้งชื่อกรณีทดสอบที่แตกต่างกันเช่นเดียวกับวิธีการตรงไปตรงมา .

ฉันมักจะพบว่าใช้วิธีการนี้ในการจัดระเบียบรหัสยูทิลิตี้ของโครงการ บ่อยครั้งที่ผู้คนรีบเร่งและสร้างutilsแพ็คเกจและจบลงด้วย 9 โมดูลซึ่งหนึ่งในนั้นมี 120 LOC และที่เหลือเป็นสองโหล LOC ที่ดีที่สุด ฉันชอบที่จะเริ่มต้นด้วยสิ่งนี้และแปลงเป็นแพ็คเกจและสร้างโมดูลสำหรับสัตว์ที่สมควรได้รับอย่างแท้จริงเท่านั้น:

# 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

บางทีตัวเลือกที่ง่ายที่สุดคือการใส่ฟังก์ชั่นเหล่านั้นนอกคลาส:

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"

การใช้วิธีการนี้ฟังก์ชันที่แก้ไขหรือใช้สถานะวัตถุภายใน (มีผลข้างเคียง) สามารถเก็บไว้ในคลาสได้และสามารถย้ายฟังก์ชั่นยูทิลิตี้ที่ใช้ซ้ำได้ภายนอก

dogs.pyสมมติว่าไฟล์นี้จะเรียกว่า หากต้องการใช้เหล่านี้ที่คุณต้องการเรียกแทนdogs.barking_sound()dogs.Dog.barking_sound

ถ้าคุณต้องการจริงๆวิธีคงเป็นส่วนหนึ่งของชั้นเรียนคุณสามารถใช้StaticMethodมัณฑนากร


1

ดังนั้นวิธีการคงที่เป็นวิธีการที่สามารถเรียกได้โดยไม่ต้องสร้างวัตถุของชั้นเรียน ตัวอย่างเช่น :-

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

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

ในตัวอย่างข้างต้นวิธีการที่addถูกเรียกโดยชื่อชั้นAไม่ใช่ชื่อวัตถุ


-3

Python Static method สามารถสร้างได้สองวิธี

  1. การใช้ staticmethod ()

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

เอาท์พุท:

ผลลัพธ์: 25

  1. ใช้ @ staticmethod

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

เอาท์พุท:

ผลลัพธ์: 25


คุณเพียงแค่ให้ตัวอย่างและแบ่งปันวิธีการที่คุณทำได้ คุณไม่ได้อธิบายความหมายของวิธีการ
zixuan

-4

ฉันพบคำถามนี้เป็นครั้งคราว กรณีการใช้งานและตัวอย่างที่ฉันชอบคือ:

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']
>>> 

มันไม่สมเหตุสมผลเลยที่จะสร้างวัตถุของคลาส cmath เนื่องจากไม่มีสถานะในวัตถุ cmath อย่างไรก็ตาม cmath เป็นชุดของวิธีการที่เกี่ยวข้องทั้งหมดในบางวิธี ในตัวอย่างของฉันข้างต้นฟังก์ชันทั้งหมดใน cmath กระทำกับตัวเลขที่ซับซ้อนในบางวิธี


คุณไม่ได้ตอบคำถาม แต่ให้ตัวอย่างแทน
zixuan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.