ฉันควรใช้ @classmethod เมื่อใดและเมื่อใดที่วิธี def (self)?


92

ในขณะที่รวมแอพ Django ที่ฉันไม่เคยใช้มาก่อนฉันพบสองวิธีที่แตกต่างกันที่ใช้ในการกำหนดฟังก์ชันในคลาส ผู้เขียนดูเหมือนจะใช้ทั้งสองอย่างตั้งใจมาก คนแรกคือตัวฉันเองใช้บ่อยมาก:

class Dummy(object):

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

อีกอันคืออันที่ฉันไม่ได้ใช้ส่วนใหญ่เป็นเพราะฉันไม่เข้าใจว่าจะใช้เมื่อใดและทำเพื่ออะไร:

class Dummy(object):

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

ในเอกสาร Python classmethodมัณฑนากรอธิบายด้วยประโยคนี้:

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

ดังนั้นฉันเดาว่าclsหมายถึงDummyตัวมันเอง ( classไม่ใช่อินสแตนซ์) ฉันไม่เข้าใจว่าทำไมสิ่งนี้ถึงมีอยู่เพราะฉันสามารถทำได้เสมอ:

type(self).do_something_with_the_class

นี่เป็นเพียงเพื่อความชัดเจนหรือว่าฉันพลาดส่วนที่สำคัญที่สุด: สิ่งที่น่ากลัวและน่าสนใจที่ไม่สามารถทำได้หากไม่มีมัน?

คำตอบ:


71

การเดาของคุณถูกต้อง - คุณเข้าใจวิธีการ classmethodทำงาน

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

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()

เกี่ยวกับการใช้สิ่งเหล่านี้กับอินสแตนซ์ : มีอย่างน้อยสองการใช้งานหลักสำหรับการเรียกวิธีการคลาสบนอินสแตนซ์:

  1. self.some_function()จะเรียกเวอร์ชันของsome_functionประเภทจริงselfแทนที่จะเป็นคลาสที่การเรียกนั้นปรากฏขึ้น (และไม่ต้องการความสนใจหากคลาสถูกเปลี่ยนชื่อ) และ
  2. ในกรณีที่some_functionจำเป็นต้องใช้โปรโตคอลบางอย่าง แต่มีประโยชน์ในการเรียกใช้อ็อบเจ็กต์คลาสเพียงอย่างเดียว

แตกต่างกับstaticmethod : staticmethodมีวิธีการในการกำหนดวิธีการที่จะทำเช่นข้อมูลที่ไม่สามารถเข้าถึงอื่นที่เรียกว่า ที่สร้างวิธีการที่ไม่ได้รับอาร์กิวเมนต์แรกโดยปริยายเลย; ดังนั้นจะไม่มีการส่งผ่านข้อมูลใด ๆ เกี่ยวกับอินสแตนซ์หรือคลาสที่เรียกใช้

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

การใช้งานหลักที่ฉันพบคือการปรับฟังก์ชันที่มีอยู่ (ซึ่งไม่คาดว่าจะได้รับ a self) ให้เป็นวิธีการในคลาส (หรือวัตถุ)


2
Nice one: ฉันชอบคำตอบที่แบ่งออกเป็นอย่างไร - ทำไม เท่าที่ฉันได้รับตอนนี้ @classmethod อนุญาตให้เข้าถึงฟังก์ชันโดยไม่จำเป็นต้องมีอินสแตนซ์ นี่คือสิ่งที่ฉันกำลังมองหาขอบคุณ
มี.ค.

1
@marcin การพิมพ์เป็ดที่แท้จริงให้มิติอื่น ๆ มันได้รับการเพิ่มเติมเกี่ยวกับการไม่ได้เขียนสิ่งที่ต้องการเมื่อมันควรจะเป็นself.foo() Bar.foo()
Voo

5
@Voo จริงself.foo()เป็นที่นิยมเพราะอาจจะเป็นตัวอย่างของประเภทรองซึ่งดำเนินการของตัวเองself foo
Marcin

1
@ Marcin ฉันคิดว่าสิ่งนี้ขึ้นอยู่กับสิ่งที่คุณต้องการบรรลุ แต่ฉันเข้าใจจุดของคุณ
มี.ค.

1
คุณควรใช้แลมบ์ดาที่แตกต่างกันสำหรับการBarเป็น1+1 == 2 == 1*2ดังนั้นจึงเป็นไปไม่ได้ที่จะบอกได้จากผลลัพธ์ที่แสดงให้เห็นว่าBar().static_methodเรียกว่าจริง
George

0

โดยพื้นฐานแล้วคุณควรใช้ @classmethod เมื่อคุณตระหนักว่าคำจำกัดความของวิธีการจะไม่ถูกเปลี่ยนแปลงหรือลบล้าง

เพิ่มเติม: ในทางทฤษฎีคลาสเมธอดจะเร็วกว่าเมธอดอ็อบเจ็กต์เนื่องจากไม่จำเป็นต้องสร้างอินสแตนซ์และต้องการหน่วยความจำน้อยลง


-3

หากคุณเพิ่มมัณฑนากร @classmethod นั่นหมายความว่าคุณจะทำให้เมธอดนั้นเป็นวิธีคงที่ของ java หรือ C ++ (วิธีการคงที่เป็นคำทั่วไปที่ฉันเดา;) ) Python ยังมี @staticmethod และความแตกต่างระหว่าง classmethod และ staticmethod คือคุณสามารถเข้าถึง class หรือ static variable โดยใช้อาร์กิวเมนต์หรือ classname เอง

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()

คลาสเหล่านั้นทั้งหมดจะเพิ่ม cls.cls_var ขึ้น 1 และพิมพ์

และทุกคลาสที่ใช้ชื่อเดียวกันในขอบเขตเดียวกันหรืออินสแตนซ์ที่สร้างด้วยคลาสเหล่านี้จะแชร์วิธีการเหล่านั้น มี TestMethod.cls_var เพียงรายการเดียวและยังมี TestMethod.class_method () เพียงรายการเดียวคือ TestMethod.static_method ()

และคำถามที่สำคัญ ทำไมต้องใช้วิธีการเหล่านี้

classmethod หรือ staticmethod มีประโยชน์เมื่อคุณสร้างคลาสนั้นเป็นโรงงานหรือเมื่อคุณต้องเริ่มต้นคลาสของคุณเพียงครั้งเดียว เช่นเปิดไฟล์ครั้งเดียวและใช้วิธีฟีดเพื่ออ่านไฟล์ทีละบรรทัด


2
(ก) วิธีการคงที่เป็นอย่างอื่น (b) ทุกชั้นเรียนจะไม่ใช้วิธีการเรียนร่วมกัน
Marcin

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