ฉันจะเข้าถึงตัวแปรคลาส“ คงที่” ภายในวิธีการเรียนใน Python ได้อย่างไร


162

ถ้าฉันมีรหัสหลามต่อไปนี้:

class Foo(object):
    bar = 1

    def bah(self):
        print(bar)

f = Foo()
f.bah()

มันบ่น

NameError: global name 'bar' is not defined

ฉันจะเข้าถึงตัวแปร class / static ได้อย่างไรbarภายใน method bah?


ข้อมูลเพิ่มเติมเกี่ยวกับงูหลามและสถิตสามารถพบได้ที่นี่: stackoverflow.com/questions/68645/python-static-variable
bedwyr

คำตอบ:


166

แทนการbarใช้หรือself.bar Foo.barการกำหนดให้กับFoo.barจะสร้างตัวแปรแบบสแตติกและการกำหนดให้กับself.barจะสร้างตัวแปรอินสแตนซ์


12
Foo.bar จะทำงานได้ แต่ self.bar จะสร้างตัวแปรอินสแตนซ์ไม่ใช่แบบคงที่
bedwyr

47
bedwyr "print self.bar" จะไม่สร้างตัวแปรอินสแตนซ์ใด ๆ (แม้ว่าจะกำหนดให้กับ self.bar จะ)
Constantin

@ Constantin - ฉันไม่ได้ตระหนักว่ามันเป็นความแตกต่างที่น่าสนใจ ขอขอบคุณสำหรับการแก้ไข :-)
bedwyr

2
แต่ถ้าคุณไม่ต้องการให้มี ivar อยู่ชัดเจนว่าใช้ Foo.classmember
mk12

2
เมื่อใช้ตัวแปรคงที่มันเป็นความคิดที่ดีในการอ่าน gotchas จากที่นี่: stackoverflow.com/questions/68645/... @Constantin ให้หนึ่ง gotchas มากมาย
Trevor Boyd Smith

84

กำหนดวิธีการเรียน:

class Foo(object):
    bar = 1
    @classmethod
    def bah(cls):    
        print cls.bar

ตอนนี้ถ้าbah()จะต้องมีวิธีการอินสแตนซ์ (เช่นมีการเข้าถึงตนเอง) คุณยังสามารถเข้าถึงตัวแปรคลาสได้โดยตรง

class Foo(object):
    bar = 1
    def bah(self):    
        print self.bar

3
ทำไมไม่เพียงแค่ Foo.bar แทนที่จะเป็นตัวเอง _class__.bar?
mk12

15
@ Mk12: เมื่อคุณได้รับการสืบทอดคลาส "ตัวแปรคลาส" อาจอยู่ในคลาสพื้นฐานหรือคลาสย่อย คุณต้องการอ้างอิงเรื่องอะไร ขึ้นอยู่กับสิ่งที่คุณพยายามจะทำ Foo.bar มักจะอ้างถึงคุณลักษณะของคลาสที่ระบุ - ซึ่งอาจเป็นคลาสพื้นฐานหรือคลาสย่อย self.__class__.bar จะอ้างถึงคลาสใดก็ตามที่อินสแตนซ์นั้นเป็นประเภทของ
Craig McQueen

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

2
BTW นี่เป็นการใช้งาน "ตัวแปรคลาส" ที่หายาก เป็นเรื่องธรรมดามากที่จะนิยามไว้ในชั้นเรียนเฉพาะที่นี่ Foo ข้อมูลนี้มีประโยชน์สำหรับสถานการณ์การเขียนโปรแกรมขั้นสูงบางอย่าง แต่แทบจะไม่ใช่สิ่งที่คำถามต้นฉบับต้องการเป็นคำตอบ (ใครก็ตามที่ต้องการคำตอบนี้จะรู้วิธีทำ Foo.bar อยู่แล้ว) +1 เพราะฉันเรียนรู้บางสิ่งจากสิ่งนี้
ToolmakerSteve

3
ฉันเรียนรู้เกี่ยวกับเวลาที่จะใช้ตัวแปรคลาสและเมธอด (เมื่อเทียบกับอินสแตนซ์ตัวแปรและวิธีการ) เมื่อคุณต้องการเข้าถึงตัวแปรคลาสในเมธอดอินสแตนซ์จำเป็นต้องใช้ตัวเองหรือไม่? class__.bar? ฉันสังเกตเห็นว่า self.bar ทำงานได้แม้ว่า bar เป็นตัวแปรคลาสไม่ใช่ตัวแปร instance
Bill

13

เช่นเดียวกับตัวอย่างที่ดีทั้งหมดคุณทำให้สิ่งที่คุณพยายามทำง่ายขึ้น นี่เป็นสิ่งที่ดี แต่มันก็คุ้มที่จะสังเกตว่าหลามนั้นมีความยืดหยุ่นอย่างมากเมื่อพูดถึงตัวแปร class กับอินสแตนซ์ เดียวกันสามารถพูดถึงวิธีการ สำหรับรายการความเป็นไปได้ที่ดีฉันขอแนะนำให้อ่านการแนะนำสไตล์แบบใหม่ของ Michael Fötschโดยเฉพาะอย่างยิ่งส่วนที่ 2 ถึง 6

สิ่งหนึ่งที่ต้องใช้ความพยายามอย่างมากในการเริ่มต้นคือpython ไม่ใช่จาวา มากกว่าแค่ถ้อยคำที่เบื่อหู ใน java คลาสทั้งหมดถูกคอมไพล์ทำให้ความละเอียดของเนมสเปซง่ายจริง: ตัวแปรใด ๆ ที่ประกาศนอกเมธอด (ที่ใดก็ได้) เป็นอินสแตนซ์ (หรือถ้าคงที่คลาส) ตัวแปรและเข้าถึงได้โดยปริยายภายในเมธอด

ด้วยไพ ธ อนกฎข้อใหญ่คือมีสามเนมสเปซที่ค้นหาตามลำดับสำหรับตัวแปร:

  1. ฟังก์ชั่น / วิธีการ
  2. โมดูลปัจจุบัน
  3. builtins

{begin pedagogy}

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

>>> class A(object):
        foo = 'foo'
        bar = foo


>>> A.foo
'foo'
>>> A.bar
'foo'

แต่:

>>> class B(object):
        foo = 'foo'
        def get_foo():
            return foo
        bar = get_foo()



Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    class B(object):
  File "<pyshell#11>", line 5, in B
    bar = get_foo()
  File "<pyshell#11>", line 4, in get_foo
    return foo
NameError: global name 'foo' is not defined

{end pedagogy}

ในท้ายที่สุดสิ่งที่ต้องจำไว้คือคุณไม่ได้มีการเข้าถึงตัวแปรใด ๆ ที่คุณต้องการเข้าถึง แต่อาจจะไม่ได้โดยปริยาย หากเป้าหมายของคุณเรียบง่ายและตรงไปตรงมาการไปหา Foo.bar หรือ self.bar อาจจะเพียงพอ หากตัวอย่างของคุณมีความซับซ้อนมากขึ้นหรือคุณต้องการทำสิ่งที่แปลกใหม่เช่นการสืบทอด (คุณสามารถสืบทอดวิธีการคงที่ / คลาส!) หรือความคิดในการอ้างถึงชื่อคลาสของคุณภายในคลาสนั้นดูเหมือนผิดกับคุณ คำนำที่ฉันเชื่อมโยง


IIRC มีการค้นหาเนมสเปซทางเทคนิค 3 (+) - ฟังก์ชันท้องถิ่น, โมดูล / ทั่วโลกและบิวด์อิน ขอบเขตที่ซ้อนกันหมายความว่าอาจมีการค้นหาขอบเขตในท้องถิ่นหลายรายการ แต่นั่นเป็นหนึ่งในกรณีพิเศษ (... )
Jeff Shannon

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


-2
class Foo(object):    
    bar = 1

    def bah(object_reference):
        object_reference.var = Foo.bar
        return object_reference.var


f = Foo() 
print 'var=', f.bah()

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