Bagaimana saya bisa mengakses variabel kelas "statis" dalam metode kelas dengan Python?

162

Jika saya memiliki kode python berikut:

class Foo(object):
    bar = 1

    def bah(self):
        print(bar)

f = Foo()
f.bah()

Itu mengeluh

NameError: global name 'bar' is not defined

Bagaimana saya bisa mengakses variabel kelas / statis bardalam metode bah?

Ross Rogers
sumber
Informasi lebih lanjut tentang Python dan statika dapat ditemukan di sini: stackoverflow.com/questions/68645/python-static-variable
bedwyr

Jawaban:

166

Alih-alih barmenggunakan self.baratau Foo.bar. Menetapkan ke Foo.barakan membuat variabel statis, dan menugaskan ke self.barakan membuat variabel contoh.

Pavel Strakhov
sumber
12
Foo.bar akan berfungsi, tetapi self.bar membuat variabel instan, bukan variabel statis.
bedwyr
47
bedwyr, "print self.bar" tidak akan membuat variabel instan (walaupun menugaskan ke self.bar akan).
Constantin
@Constantin - Saya tidak menyadarinya, ini adalah perbedaan yang menarik. Terima kasih atas koreksinya :-)
bedwyr
2
Tetapi jika Anda tidak berniat untuk membuat ivar, lebih jelas untuk menggunakan Foo.classmember.
mk12
2
ketika menggunakan variabel statis, adalah ide yang bagus untuk membaca Gotcha dari sini: stackoverflow.com/questions/68645/… . @Constantin memberikan salah satu dari banyak gotcha.
Trevor Boyd Smith
84

Tentukan metode kelas:

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

Sekarang jika bah()harus metode instan (yaitu memiliki akses ke diri), Anda masih dapat langsung mengakses variabel kelas.

class Foo(object):
    bar = 1
    def bah(self):    
        print self.bar
vartec
sumber
3
Mengapa tidak hanya Foo.bar, bukan self.__class__.bar?
mk12
15
@ Mk12: Ketika Anda mendapatkan warisan kelas, "variabel kelas" bisa berada di kelas dasar atau subkelas. Anda ingin merujuk ke mana? Tergantung pada apa yang Anda coba lakukan. Foo.bar akan selalu merujuk ke atribut dari kelas yang ditentukan - yang mungkin merupakan kelas dasar atau subkelas. self.__class__.bar akan merujuk ke kelas mana pun yang merupakan instance dari kelas.
Craig McQueen
7
Sekarang kita telah mencapai titik yang disayangkan ketika kita menjadi begitu sadar akan detail bahasa sehingga kita dapat membedakan antara dua kasus penggunaan yang diselaraskan dengan baik. Memang tergantung apa yang ingin Anda lakukan, tetapi banyak orang tidak akan memperhatikan kehalusan tersebut.
holdenweb
2
BTW, ini adalah penggunaan RARE "variabel kelas". Jauh lebih umum untuk mendefinisikannya dalam kelas tertentu, di sini Foo. Ini adalah informasi yang berguna untuk beberapa situasi pemrograman tingkat lanjut. Tapi hampir pasti bukan yang diinginkan pertanyaan asli sebagai jawaban (siapa pun yang membutuhkan jawaban INI sudah akan tahu bagaimana melakukan Foo.bar). +1 karena saya belajar sesuatu dari ini.
ToolmakerSteve
3
Saya belajar tentang kapan harus menggunakan variabel dan metode kelas (sebagai lawan variabel instan dan metode). Ketika Anda ingin mengakses variabel kelas dalam metode instance, apakah perlu menggunakan self.__class__.bar? Saya perhatikan bahwa self.bar bekerja walaupun bilah adalah variabel kelas bukan variabel contoh.
Bill
13

Seperti halnya semua contoh bagus, Anda telah menyederhanakan apa yang sebenarnya Anda coba lakukan. Ini bagus, tetapi perlu dicatat bahwa python memiliki banyak fleksibilitas ketika datang ke variabel class versus instance. Hal yang sama dapat dikatakan tentang metode. Untuk daftar kemungkinan yang bagus, saya sarankan membaca pengantar kelas gaya baru Michael Fötsch , terutama bagian 2 hingga 6.

Satu hal yang membutuhkan banyak pekerjaan untuk diingat ketika memulai adalah bahwa python bukan java. Lebih dari sekadar klise. Di java, seluruh kelas dikompilasi, membuat resolusi namespace menjadi sangat sederhana: variabel apa pun yang dideklarasikan di luar metode (di mana saja) adalah variabel instance (atau, jika statis, kelas) dan dapat diakses secara implisit dalam metode.

Dengan python, aturan utama adalah bahwa ada tiga ruang nama yang dicari, secara berurutan, untuk variabel:

  1. Fungsi / metode
  2. Modul saat ini
  3. Dibangun

{begin pedagogy}

Ada pengecualian terbatas untuk ini. Yang utama yang terjadi pada saya adalah bahwa, ketika definisi kelas sedang dimuat, definisi kelas adalah namespace implisit sendiri. Tapi ini hanya berlangsung selama modul dimuat, dan sepenuhnya dilewati ketika berada dalam suatu metode. Jadi:

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


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

tapi:

>>> 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}

Pada akhirnya, hal yang perlu diingat adalah bahwa Anda tidak memiliki akses ke salah satu variabel yang ingin Anda akses, tapi mungkin tidak secara implisit. Jika sasaran Anda sederhana dan langsung, maka gunakan Foo.bar atau self.bar mungkin akan cukup. Jika contoh Anda menjadi lebih rumit, atau Anda ingin melakukan hal-hal mewah seperti warisan (Anda dapat mewarisi metode statis / kelas!), Atau gagasan merujuk nama kelas Anda di dalam kelas itu sendiri tampaknya salah bagi Anda, periksa intro yang saya tautkan.

David Berger
sumber
IIRC, secara teknis ada 3 (+) ruang nama yang dicari - function-local, module / global, dan builtin. Lingkup bersarang berarti bahwa beberapa lingkup lokal dapat dicari, tetapi itulah salah satu kasus luar biasa. (...)
Jeff Shannon
Juga, saya akan berhati-hati mengatakan 'modul utama', karena fungsi yang berisi modul yang dicari, bukan modul utama ... Dan mencari atribut dari contoh referensi adalah hal yang berbeda, tetapi jawaban ini tidak menjelaskan mengapa Anda memerlukan instance / class ref.
Jeff Shannon
10
class Foo(object):
     bar = 1
     def bah(self):
         print Foo.bar

f = Foo() 
f.bah()
Allan Mwesigwa
sumber
-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()
Lopi Dani
sumber
4
Meskipun kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan tentang bagaimana dan mengapa ini menyelesaikan masalah akan sangat membantu untuk meningkatkan kualitas posting Anda, dan mungkin menghasilkan lebih banyak suara. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, bukan hanya orang yang bertanya sekarang. Harap edit jawaban Anda untuk menambahkan penjelasan dan berikan indikasi tentang batasan dan asumsi apa yang berlaku. Dari Ulasan
double-beep