Apakah variabel kelas statis dimungkinkan dengan Python?

1949

Apakah mungkin untuk memiliki variabel atau metode kelas statis di Python? Sintaks apa yang diperlukan untuk melakukan ini?

Andrew Walker
sumber

Jawaban:

1900

Variabel yang dideklarasikan di dalam definisi kelas, tetapi tidak di dalam metode adalah variabel kelas atau statis:

>>> class MyClass:
...     i = 3
...
>>> MyClass.i
3 

Seperti yang ditunjukkan oleh @ millerdev , ini menciptakan ivariabel level kelas , tetapi ini berbeda dari ivariabel level instance mana pun , sehingga Anda dapat memiliki

>>> m = MyClass()
>>> m.i = 4
>>> MyClass.i, m.i
>>> (3, 4)

Ini berbeda dari C ++ dan Java, tetapi tidak begitu berbeda dari C #, di mana anggota statis tidak dapat diakses menggunakan referensi ke instance.

Lihat apa yang dikatakan tutorial Python tentang subjek kelas dan objek kelas .

@Steve Johnson telah menjawab mengenai metode statis , juga didokumentasikan dalam "Fungsi Bawaan " di Referensi Pustaka Python .

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

@beidy merekomendasikan classmethod s over staticmethod, karena metode ini kemudian menerima tipe class sebagai argumen pertama, tapi saya masih sedikit kabur tentang kelebihan pendekatan ini atas staticmethod. Jika Anda juga, maka itu mungkin tidak masalah.

Blair Conrad
sumber
11
Aku hanya belajar Python, tapi keuntungan dari @classmethodlebih @staticmethodAFAIK adalah bahwa Anda selalu mendapatkan nama kelas metode ini dipanggil pada, bahkan jika itu subclass. Metode statis tidak memiliki informasi ini, jadi tidak dapat memanggil metode yang diganti, misalnya.
Seb
49
@theJollySin cara pythonic untuk konstanta adalah untuk tidak menumbuhkan kelas untuk konstanta. Hanya const.pydengan beberapa PI = 3.14dan Anda dapat mengimpornya di mana saja. from const import PI
Giszmo
30
Jawaban ini cenderung membingungkan masalah variabel statis. Pertama-tama, i = 3ini bukan variabel statis, itu adalah atribut kelas, dan karena ia berbeda dari atribut tingkat instancei ia tidak berperilaku seperti variabel statis dalam bahasa lain. Lihat jawaban millerdev ini , jawaban Yann ini , dan jawaban saya di bawah ini.
Rick mendukung Monica
2
jadi hanya satu salinan i(variabel statis) akan berada di memori bahkan jika saya membuat ratusan instance dari kelas ini?
sdream
2
Bagi siapa pun yang tertarik siapa yang disebutkan Daniel dalam komentar @Dubslow, itu millerdev ( mesin
wayback
619

@ Blair Conrad mengatakan variabel statis dideklarasikan di dalam definisi kelas, tetapi tidak di dalam metode adalah variabel kelas atau "statis":

>>> class Test(object):
...     i = 3
...
>>> Test.i
3

Ada beberapa gotcha di sini. Melanjutkan dari contoh di atas:

>>> t = Test()
>>> t.i     # "static" variable accessed via instance
3
>>> t.i = 5 # but if we assign to the instance ...
>>> Test.i  # we have not changed the "static" variable
3
>>> t.i     # we have overwritten Test.i on t by creating a new attribute t.i
5
>>> Test.i = 6 # to change the "static" variable we do it by assigning to the class
>>> t.i
5
>>> Test.i
6
>>> u = Test()
>>> u.i
6           # changes to t do not affect new instances of Test

# Namespaces are one honking great idea -- let's do more of those!
>>> Test.__dict__
{'i': 6, ...}
>>> t.__dict__
{'i': 5}
>>> u.__dict__
{}

Perhatikan bagaimana variabel instan t.ikeluar dari sinkronisasi dengan variabel kelas "statis" ketika atribut idisetel langsung t. Ini karena iterikat kembali di dalam tnamespace, yang berbeda dari Testnamespace. Jika Anda ingin mengubah nilai variabel "statis", Anda harus mengubahnya dalam ruang lingkup (atau objek) di mana ia awalnya didefinisikan. Saya menaruh "statis" dalam tanda kutip karena Python tidak benar-benar memiliki variabel statis dalam arti bahwa C ++ dan Java lakukan.

Meskipun tidak mengatakan sesuatu yang spesifik tentang variabel atau metode statis, tutorial Python memiliki beberapa informasi yang relevan tentang kelas dan objek kelas .

@Steve Johnson juga menjawab mengenai metode statis, juga didokumentasikan dalam "Fungsi Bawaan" di Referensi Pustaka Python.

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

@beid juga menyebutkan classmethod, yang mirip dengan staticmethod. Argumen pertama classmethod adalah objek kelas. Contoh:

class Test(object):
    i = 3 # class (or static) variable
    @classmethod
    def g(cls, arg):
        # here we can use 'cls' instead of the class name (Test)
        if arg > cls.i:
            cls.i = arg # would be the same as Test.i = arg1

Representasi Bergambar Dari Contoh Di Atas

millerdev
sumber
3
Saya sarankan Anda memperluas contoh hanya sedikit: jika, setelah menetapkan Test.i = 6, Anda kemudian instantiate objek baru (misalnya, u = Test ()), objek baru akan "mewarisi" nilai kelas baru (misalnya, ui == 6)
Mark
2
Sebuah cara untuk menjaga variabel statis sinkron adalah untuk membuat mereka sifat: class Test(object):, _i = 3, @property, def i(self), return type(self)._i, @i.setter, def i(self,val):, type(self)._i = val. Sekarang Anda dapat melakukan x = Test(), x.i = 12, assert x.i == Test.i.
Rick mendukung Monica
1
Jadi saya bisa mengatakan semua variabel awalnya statis dan kemudian mengakses instance membuat variabel instan saat runtime?
Ali
Mungkin ini menarik: jika Anda mendefinisikan metode dalam Tes yang mengubah Test.i, itu akan mempengaruhi KEDUA nilai Test.i dan ti.
Pablo
@ Millerdev, seperti yang Anda sebutkan Python tidak memiliki variabel statis seperti C ++ atau JAVA miliki..Jadi tidak apa-apa untuk mengatakan, Test.i lebih merupakan variabel kelas daripada variabel statis?
Tyto
197

Metode Statis dan Kelas

Seperti yang dicatat oleh jawaban lain, metode statis dan kelas mudah dilakukan dengan menggunakan dekorator bawaan:

class Test(object):

    # regular instance method:
    def MyMethod(self):
        pass

    # class method:
    @classmethod
    def MyClassMethod(klass):
        pass

    # static method:
    @staticmethod
    def MyStaticMethod():
        pass

Seperti biasa, argumen pertama MyMethod()terikat ke objek instance kelas. Sebaliknya, argumen pertama untuk MyClassMethod()yang terikat ke objek kelas itu sendiri (misalnya, dalam kasus ini, Test). Sebab MyStaticMethod(), tidak ada argumen yang terikat, dan memiliki argumen sama sekali adalah opsional.

"Variabel Statis"

Namun, menerapkan "variabel statis" (yah, variabel statis yang dapat berubah , bagaimanapun, jika itu bukan kontradiksi dalam istilah ...) tidak semudah itu. Seperti yang ditunjukkan millerdev dalam jawabannya , masalahnya adalah atribut kelas Python tidak benar-benar "variabel statis". Mempertimbangkan:

class Test(object):
    i = 3  # This is a class attribute

x = Test()
x.i = 12   # Attempt to change the value of the class attribute using x instance
assert x.i == Test.i  # ERROR
assert Test.i == 3    # Test.i was not affected
assert x.i == 12      # x.i is a different object than Test.i

Ini karena baris x.i = 12telah menambahkan atribut instance baru iuntuk xbukannya mengubah nilai Testkelasi atribut .

Perilaku variabel statis parsial yang diharapkan, yaitu, sinkronisasi atribut antara beberapa instance (tetapi tidak dengan kelas itu sendiri; lihat "gotcha" di bawah), dapat dicapai dengan mengubah atribut kelas menjadi properti:

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

    @i.setter
    def i(self,val):
        type(self)._i = val

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting and setting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    def set_i(self,val):
        type(self)._i = val

    i = property(get_i, set_i)

Sekarang Anda bisa melakukannya:

x1 = Test()
x2 = Test()
x1.i = 50
assert x2.i == x1.i  # no error
assert x2.i == 50    # the property is synced

Variabel statis sekarang akan tetap sinkron antara semua instance kelas .

(CATATAN: Yaitu, kecuali instance kelas memutuskan untuk mendefinisikan versinya sendiri _i ! Tetapi jika seseorang memutuskan untuk melakukan ITU, mereka layak mendapatkan apa yang mereka dapatkan, bukankah begitu ???)

Perhatikan bahwa secara teknis, imasih bukan 'variabel statis' sama sekali; itu adalah property, yang merupakan tipe deskriptor khusus. Namun demikianproperty perilaku sekarang setara dengan variabel statis (dapat diubah) disinkronkan di semua instance kelas.

"Variabel Statis" yang Tidak Berubah

Untuk perilaku variabel statis yang tidak dapat diubah, cukup abaikan propertysetter:

class Test(object):

    _i = 3

    @property
    def i(self):
        return type(self)._i

## ALTERNATIVE IMPLEMENTATION - FUNCTIONALLY EQUIVALENT TO ABOVE ##
## (except with separate methods for getting i) ##

class Test(object):

    _i = 3

    def get_i(self):
        return type(self)._i

    i = property(get_i)

Sekarang mencoba untuk mengatur iatribut instance akan mengembalikan AttributeError:

x = Test()
assert x.i == 3  # success
x.i = 12         # ERROR

Satu Gotcha menjadi Sadar

Perhatikan bahwa metode di atas hanya berfungsi dengan instance kelas Anda - mereka tidak akan berfungsi saat menggunakan kelas itu sendiri . Jadi misalnya:

x = Test()
assert x.i == Test.i  # ERROR

# x.i and Test.i are two different objects:
type(Test.i)  # class 'property'
type(x.i)     # class 'int'

Garis assert Test.i == x.imenghasilkan kesalahan, karena iatribut Testdan xdua objek berbeda.

Banyak orang akan menganggap ini mengejutkan. Namun, seharusnya tidak demikian. Jika kita kembali dan memeriksa Testdefinisi kelas kita (versi kedua), kita perhatikan baris ini:

    i = property(get_i) 

Jelas, anggota idari Testkeharusan menjadi propertyobjek, yang merupakan jenis objek kembali dari propertyfungsi.

Jika Anda menemukan hal di atas membingungkan, kemungkinan besar Anda masih memikirkannya dari perspektif bahasa lain (mis. Java atau c ++). Anda harus belajarproperty objek tersebut, tentang urutan pengembalian atribut Python, protokol deskriptor, dan urutan resolusi metode (MRO).

Saya menyajikan solusi untuk 'gotcha' di atas di bawah ini; namun saya akan menyarankan - sangat - bahwa Anda tidak mencoba melakukan sesuatu seperti berikut sampai - minimal - Anda benar-benar mengerti mengapa assert Test.i = x.imenyebabkan kesalahan.

Variabel Statis NYATA, AKTUAL -Test.i == x.i

Saya menyajikan solusi (Python 3) di bawah ini hanya untuk tujuan informasi. Saya tidak mendukungnya sebagai "solusi yang baik". Saya ragu apakah meniru perilaku variabel statis bahasa lain di Python benar-benar diperlukan. Namun, terlepas dari apakah itu benar-benar berguna, di bawah ini akan membantu pemahaman lebih lanjut tentang cara kerja Python.

UPDATE: upaya ini sangat mengerikan ; jika Anda bersikeras melakukan sesuatu seperti ini (petunjuk: tolong jangan; Python adalah bahasa yang sangat elegan dan menganggapnya seperti bahasa lain tidak perlu), gunakan kode dalam jawaban Ethan Furman sebagai gantinya.

Meniru perilaku variabel statis dari bahasa lain menggunakan metaclass

Metaclass adalah kelas suatu kelas. Metaclass default untuk semua kelas di Python (yaitu, kelas "gaya baru" memposting Python 2.3 saya percaya) adalah type. Sebagai contoh:

type(int)  # class 'type'
type(str)  # class 'type'
class Test(): pass
type(Test) # class 'type'

Namun, Anda dapat mendefinisikan metaclass Anda sendiri seperti ini:

class MyMeta(type): pass

Dan terapkan ke kelas Anda sendiri seperti ini (hanya Python 3):

class MyClass(metaclass = MyMeta):
    pass

type(MyClass)  # class MyMeta

Di bawah ini adalah metaclass yang saya buat yang mencoba meniru perilaku "variabel statis" dari bahasa lain. Ini pada dasarnya bekerja dengan mengganti pengambil default, setter, dan deleter dengan versi yang memeriksa untuk melihat apakah atribut yang diminta adalah "variabel statis".

Katalog "variabel statis" disimpan dalam StaticVarMeta.staticsatribut. Semua permintaan atribut pada awalnya dicoba untuk diselesaikan menggunakan urutan resolusi pengganti. Saya telah menjuluki ini "urutan resolusi statis", atau "SRO". Ini dilakukan dengan mencari atribut yang diminta dalam set "variabel statis" untuk kelas tertentu (atau kelas induknya). Jika atribut tidak muncul di "SRO", kelas akan kembali pada atribut default dapatkan / atur / hapus perilaku (yaitu, "MRO").

from functools import wraps

class StaticVarsMeta(type):
    '''A metaclass for creating classes that emulate the "static variable" behavior
    of other languages. I do not advise actually using this for anything!!!

    Behavior is intended to be similar to classes that use __slots__. However, "normal"
    attributes and __statics___ can coexist (unlike with __slots__). 

    Example usage: 

        class MyBaseClass(metaclass = StaticVarsMeta):
            __statics__ = {'a','b','c'}
            i = 0  # regular attribute
            a = 1  # static var defined (optional)

        class MyParentClass(MyBaseClass):
            __statics__ = {'d','e','f'}
            j = 2              # regular attribute
            d, e, f = 3, 4, 5  # Static vars
            a, b, c = 6, 7, 8  # Static vars (inherited from MyBaseClass, defined/re-defined here)

        class MyChildClass(MyParentClass):
            __statics__ = {'a','b','c'}
            j = 2  # regular attribute (redefines j from MyParentClass)
            d, e, f = 9, 10, 11   # Static vars (inherited from MyParentClass, redefined here)
            a, b, c = 12, 13, 14  # Static vars (overriding previous definition in MyParentClass here)'''
    statics = {}
    def __new__(mcls, name, bases, namespace):
        # Get the class object
        cls = super().__new__(mcls, name, bases, namespace)
        # Establish the "statics resolution order"
        cls.__sro__ = tuple(c for c in cls.__mro__ if isinstance(c,mcls))

        # Replace class getter, setter, and deleter for instance attributes
        cls.__getattribute__ = StaticVarsMeta.__inst_getattribute__(cls, cls.__getattribute__)
        cls.__setattr__ = StaticVarsMeta.__inst_setattr__(cls, cls.__setattr__)
        cls.__delattr__ = StaticVarsMeta.__inst_delattr__(cls, cls.__delattr__)
        # Store the list of static variables for the class object
        # This list is permanent and cannot be changed, similar to __slots__
        try:
            mcls.statics[cls] = getattr(cls,'__statics__')
        except AttributeError:
            mcls.statics[cls] = namespace['__statics__'] = set() # No static vars provided
        # Check and make sure the statics var names are strings
        if any(not isinstance(static,str) for static in mcls.statics[cls]):
            typ = dict(zip((not isinstance(static,str) for static in mcls.statics[cls]), map(type,mcls.statics[cls])))[True].__name__
            raise TypeError('__statics__ items must be strings, not {0}'.format(typ))
        # Move any previously existing, not overridden statics to the static var parent class(es)
        if len(cls.__sro__) > 1:
            for attr,value in namespace.items():
                if attr not in StaticVarsMeta.statics[cls] and attr != ['__statics__']:
                    for c in cls.__sro__[1:]:
                        if attr in StaticVarsMeta.statics[c]:
                            setattr(c,attr,value)
                            delattr(cls,attr)
        return cls
    def __inst_getattribute__(self, orig_getattribute):
        '''Replaces the class __getattribute__'''
        @wraps(orig_getattribute)
        def wrapper(self, attr):
            if StaticVarsMeta.is_static(type(self),attr):
                return StaticVarsMeta.__getstatic__(type(self),attr)
            else:
                return orig_getattribute(self, attr)
        return wrapper
    def __inst_setattr__(self, orig_setattribute):
        '''Replaces the class __setattr__'''
        @wraps(orig_setattribute)
        def wrapper(self, attr, value):
            if StaticVarsMeta.is_static(type(self),attr):
                StaticVarsMeta.__setstatic__(type(self),attr, value)
            else:
                orig_setattribute(self, attr, value)
        return wrapper
    def __inst_delattr__(self, orig_delattribute):
        '''Replaces the class __delattr__'''
        @wraps(orig_delattribute)
        def wrapper(self, attr):
            if StaticVarsMeta.is_static(type(self),attr):
                StaticVarsMeta.__delstatic__(type(self),attr)
            else:
                orig_delattribute(self, attr)
        return wrapper
    def __getstatic__(cls,attr):
        '''Static variable getter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                try:
                    return getattr(c,attr)
                except AttributeError:
                    pass
        raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
    def __setstatic__(cls,attr,value):
        '''Static variable setter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                setattr(c,attr,value)
                break
    def __delstatic__(cls,attr):
        '''Static variable deleter'''
        for c in cls.__sro__:
            if attr in StaticVarsMeta.statics[c]:
                try:
                    delattr(c,attr)
                    break
                except AttributeError:
                    pass
        raise AttributeError(cls.__name__ + " object has no attribute '{0}'".format(attr))
    def __delattr__(cls,attr):
        '''Prevent __sro__ attribute from deletion'''
        if attr == '__sro__':
            raise AttributeError('readonly attribute')
        super().__delattr__(attr)
    def is_static(cls,attr):
        '''Returns True if an attribute is a static variable of any class in the __sro__'''
        if any(attr in StaticVarsMeta.statics[c] for c in cls.__sro__):
            return True
        return False
Rick mendukung Monica
sumber
Saya mencoba menggunakan cara Anda tetapi saya menghadapi masalah, silakan lihat pertanyaan saya di sini stackoverflow.com/questions/29329850/get-static-variable-value
Muhammed Refaat
@ RickTeachey: Saya kira Anda biasanya harus melihat apa pun yang Anda lakukan di Instance kelas Test(sebelum menggunakannya untuk instance instantiating) sebagai berada di domain meta-programming? Misalnya, Anda mengubah perilaku kelas dengan melakukan Test.i = 0(di sini Anda hanya menghancurkan objek properti sepenuhnya). Saya kira "mekanisme properti" hanya menendang pada akses-properti pada instance kelas (kecuali jika Anda mengubah perilaku mendasar menggunakan meta-class sebagai perantara, mungkin). Btw, tolong selesaikan jawaban ini :-)
Ole Thomsen Buus
1
@ RickTeachey Terima kasih :-) Metaclass Anda pada akhirnya menarik tetapi sebenarnya agak terlalu rumit untuk seleraku. Ini mungkin berguna dalam kerangka kerja / aplikasi besar di mana mekanisme ini mutlak diperlukan. Bagaimanapun, ini mencontohkan bahwa jika meta-perilaku non-standar baru (kompleks) benar-benar diperlukan, Python memungkinkan :)
Ole Thomsen Buus
1
@OleThomsenBuus: Periksa jawaban saya untuk metaclass sederhana yang melakukan pekerjaan.
Ethan Furman
1
@taper Anda benar; Saya telah mengedit jawaban untuk memperbaiki masalah (tidak bisa percaya sudah lama duduk di sana salah!). Maaf bila membingungkan.
Rick mendukung Monica
33

Anda juga dapat menambahkan variabel kelas ke kelas on the fly

>>> class X:
...     pass
... 
>>> X.bar = 0
>>> x = X()
>>> x.bar
0
>>> x.foo
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
AttributeError: X instance has no attribute 'foo'
>>> X.foo = 1
>>> x.foo
1

Dan instance kelas dapat mengubah variabel kelas

class X:
  l = []
  def __init__(self):
    self.l.append(1)

print X().l
print X().l

>python test.py
[1]
[1, 1]
Gregory
sumber
3
Apakah variabel kelas baru akan tetap ada meskipun kelas diimpor ke modul lain?
zakdances
Iya. Kelas secara efektif adalah lajang, terlepas dari namespace dari mana Anda memanggilnya.
Pedro
@Gregory Anda berkata "Dan instance kelas dapat mengubah variabel kelas" Sebenarnya contoh ini disebut akses bukan modifikasi. Modifikasi dilakukan oleh objek itu sendiri melalui fungsi append () sendiri.
Amr ALHOSSARY
19

Secara pribadi saya akan menggunakan classmethod setiap kali saya membutuhkan metode statis. Terutama karena saya mendapatkan kelas sebagai argumen.

class myObj(object):
   def myMethod(cls)
     ...
   myMethod = classmethod(myMethod) 

atau menggunakan dekorator

class myObj(object):
   @classmethod
   def myMethod(cls)

Untuk properti statis .. Saatnya Anda mencari beberapa definisi python .. variabel selalu dapat berubah. Ada dua jenis dari mereka bisa berubah dan tidak berubah .. Juga, ada atribut kelas dan atribut contoh .. Tidak ada yang benar-benar seperti atribut statis dalam arti java & c ++

Mengapa menggunakan metode statis dalam arti pythonic, jika tidak memiliki hubungan apa pun dengan kelas! Jika saya jadi Anda, saya akan menggunakan metode classmet atau mendefinisikan metode independen dari kelas.

emb
sumber
1
Variabel tidak bisa berubah atau berubah; benda adalah. (Namun, sebuah objek dapat, dengan berbagai tingkat keberhasilan, mencoba untuk mencegah penugasan ke atribut tertentu.)
Davis Herring
Java dan C ++ menggunakan static (ill use of the word, imho) persis seperti Anda menggunakan instance versus atribut class. Atribut / metode kelas statis di Java dan C ++, tidak ada perbedaan, kecuali bahwa dalam Python parameter pertama untuk pemanggilan metode kelas adalah kelas.
Angel O'Sphere
16

Satu hal khusus yang perlu diperhatikan tentang properti statis & properti instan, ditunjukkan dalam contoh di bawah ini:

class my_cls:
  my_prop = 0

#static property
print my_cls.my_prop  #--> 0

#assign value to static property
my_cls.my_prop = 1 
print my_cls.my_prop  #--> 1

#access static property thru' instance
my_inst = my_cls()
print my_inst.my_prop #--> 1

#instance property is different from static property 
#after being assigned a value
my_inst.my_prop = 2
print my_cls.my_prop  #--> 1
print my_inst.my_prop #--> 2

Ini berarti sebelum menetapkan nilai ke properti instance, jika kami mencoba mengakses properti melalui instance, nilai statis digunakan. Setiap properti yang dideklarasikan dalam kelas python selalu memiliki slot statis di memori .

jondinham
sumber
16

Metode statis dalam python disebut classmethod s. Lihatlah kode berikut

class MyClass:

    def myInstanceMethod(self):
        print 'output from an instance method'

    @classmethod
    def myStaticMethod(cls):
        print 'output from a static method'

>>> MyClass.myInstanceMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method myInstanceMethod() must be called [...]

>>> MyClass.myStaticMethod()
output from a static method

Perhatikan bahwa ketika kita memanggil metode myInstanceMethod , kita mendapatkan kesalahan. Ini karena membutuhkan metode yang dipanggil pada turunan dari kelas ini. Metode myStaticMethod ditetapkan sebagai metode kelas menggunakan dekorator @classmethod .

Hanya untuk iseng dan cekikikan, kita bisa memanggil myInstanceMethod di kelas dengan mengirimkan instance kelas, seperti:

>>> MyClass.myInstanceMethod(MyClass())
output from an instance method
Willurd
sumber
2
Umm ... metode statis dibuat dengan @staticmethod; @classmethodadalah (jelas) untuk metode kelas (yang terutama dimaksudkan untuk digunakan sebagai konstruktor alternatif, tetapi dapat berfungsi dalam keadaan darurat sebagai metode statis yang kebetulan menerima referensi ke kelas yang mereka dipanggil melalui).
ShadowRanger
11

Ketika mendefinisikan beberapa variabel anggota di luar metode anggota, variabel dapat berupa statis atau non-statis tergantung pada bagaimana variabel tersebut diekspresikan.

  • CLASSNAME.var adalah variabel statis
  • INSTANCENAME.var bukan variabel statis.
  • kelas self.var di dalam bukanlah variabel statis.
  • var di dalam fungsi anggota kelas tidak didefinisikan.

Sebagai contoh:

#!/usr/bin/python

class A:
    var=1

    def printvar(self):
        print "self.var is %d" % self.var
        print "A.var is %d" % A.var


    a = A()
    a.var = 2
    a.printvar()

    A.var = 3
    a.printvar()

Hasilnya

self.var is 2
A.var is 1
self.var is 2
A.var is 3
pengguna2209576
sumber
Lekukan rusak. Ini tidak akan mengeksekusi
Thomas Weller
9

Dimungkinkan untuk memiliki staticvariabel kelas, tetapi mungkin tidak sepadan dengan usaha.

Berikut adalah bukti konsep yang ditulis dalam Python 3 - jika ada detail yang salah, kode dapat diubah agar sesuai dengan apa pun yang Anda maksud dengan static variable:


class Static:
    def __init__(self, value, doc=None):
        self.deleted = False
        self.value = value
        self.__doc__ = doc
    def __get__(self, inst, cls=None):
        if self.deleted:
            raise AttributeError('Attribute not set')
        return self.value
    def __set__(self, inst, value):
        self.deleted = False
        self.value = value
    def __delete__(self, inst):
        self.deleted = True

class StaticType(type):
    def __delattr__(cls, name):
        obj = cls.__dict__.get(name)
        if isinstance(obj, Static):
            obj.__delete__(name)
        else:
            super(StaticType, cls).__delattr__(name)
    def __getattribute__(cls, *args):
        obj = super(StaticType, cls).__getattribute__(*args)
        if isinstance(obj, Static):
            obj = obj.__get__(cls, cls.__class__)
        return obj
    def __setattr__(cls, name, val):
        # check if object already exists
        obj = cls.__dict__.get(name)
        if isinstance(obj, Static):
            obj.__set__(name, val)
        else:
            super(StaticType, cls).__setattr__(name, val)

dan digunakan:

class MyStatic(metaclass=StaticType):
    """
    Testing static vars
    """
    a = Static(9)
    b = Static(12)
    c = 3

class YourStatic(MyStatic):
    d = Static('woo hoo')
    e = Static('doo wop')

dan beberapa tes:

ms1 = MyStatic()
ms2 = MyStatic()
ms3 = MyStatic()
assert ms1.a == ms2.a == ms3.a == MyStatic.a
assert ms1.b == ms2.b == ms3.b == MyStatic.b
assert ms1.c == ms2.c == ms3.c == MyStatic.c
ms1.a = 77
assert ms1.a == ms2.a == ms3.a == MyStatic.a
ms2.b = 99
assert ms1.b == ms2.b == ms3.b == MyStatic.b
MyStatic.a = 101
assert ms1.a == ms2.a == ms3.a == MyStatic.a
MyStatic.b = 139
assert ms1.b == ms2.b == ms3.b == MyStatic.b
del MyStatic.b
for inst in (ms1, ms2, ms3):
    try:
        getattr(inst, 'b')
    except AttributeError:
        pass
    else:
        print('AttributeError not raised on %r' % attr)
ms1.c = 13
ms2.c = 17
ms3.c = 19
assert ms1.c == 13
assert ms2.c == 17
assert ms3.c == 19
MyStatic.c = 43
assert ms1.c == 13
assert ms2.c == 17
assert ms3.c == 19

ys1 = YourStatic()
ys2 = YourStatic()
ys3 = YourStatic()
MyStatic.b = 'burgler'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.b
assert ys1.d == ys2.d == ys3.d == YourStatic.d
assert ys1.e == ys2.e == ys3.e == YourStatic.e
ys1.a = 'blah'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
ys2.b = 'kelp'
assert ys1.b == ys2.b == ys3.b == YourStatic.b == MyStatic.b
ys1.d = 'fee'
assert ys1.d == ys2.d == ys3.d == YourStatic.d
ys2.e = 'fie'
assert ys1.e == ys2.e == ys3.e == YourStatic.e
MyStatic.a = 'aargh'
assert ys1.a == ys2.a == ys3.a == YourStatic.a == MyStatic.a
Ethan Furman
sumber
8

Anda juga bisa memaksakan kelas menjadi statis menggunakan metaclass.

class StaticClassError(Exception):
    pass


class StaticClass:
    __metaclass__ = abc.ABCMeta

    def __new__(cls, *args, **kw):
        raise StaticClassError("%s is a static class and cannot be initiated."
                                % cls)

class MyClass(StaticClass):
    a = 1
    b = 3

    @staticmethod
    def add(x, y):
        return x+y

Kemudian setiap kali secara tidak sengaja Anda mencoba menginisialisasi MyClass Anda akan mendapatkan StaticClassError.

Bartosz Ptaszynski
sumber
4
Mengapa itu bahkan sebuah kelas jika Anda tidak akan instantiate? Ini terasa seperti memutar Python untuk mengubahnya menjadi Java ....
Ned Batchelder
1
The Borg idiom adalah cara yang lebih baik untuk menangani hal ini.
Rick mendukung Monica
@NedBatchelder Ini adalah kelas abstrak, dimaksudkan hanya untuk subclassing (dan instantiating subclasses)
stevepastelan
1
Saya harap subclass tidak menggunakan super () untuk memanggil orang __new__tuanya ...
Ned Batchelder
7

Satu hal yang sangat menarik tentang pencarian atribut Python adalah ia dapat digunakan untuk membuat " variabel virtual ":

class A(object):

  label="Amazing"

  def __init__(self,d): 
      self.data=d

  def say(self): 
      print("%s %s!"%(self.label,self.data))

class B(A):
  label="Bold"  # overrides A.label

A(5).say()      # Amazing 5!
B(3).say()      # Bold 3!

Biasanya tidak ada tugas untuk ini setelah dibuat. Perhatikan bahwa pencarian menggunakan selfkarena, meskipun labelstatis dalam arti tidak dikaitkan dengan contoh tertentu , nilainya masih tergantung pada instance (kelas dari).

Davis Herring
sumber
6

Sehubungan dengan jawaban ini , untuk variabel statis konstan , Anda dapat menggunakan deskriptor. Ini sebuah contoh:

class ConstantAttribute(object):
    '''You can initialize my value but not change it.'''
    def __init__(self, value):
        self.value = value

    def __get__(self, obj, type=None):
        return self.value

    def __set__(self, obj, val):
        pass


class Demo(object):
    x = ConstantAttribute(10)


class SubDemo(Demo):
    x = 10


demo = Demo()
subdemo = SubDemo()
# should not change
demo.x = 100
# should change
subdemo.x = 100
print "small demo", demo.x
print "small subdemo", subdemo.x
print "big demo", Demo.x
print "big subdemo", SubDemo.x

yang menghasilkan ...

small demo 10
small subdemo 100
big demo 10
big subdemo 10

Anda selalu dapat mengajukan pengecualian jika mengabaikan nilai pengaturan (di passatas) dengan diam-diam bukan pilihan Anda. Jika Anda mencari C ++, variabel kelas statis gaya Java:

class StaticAttribute(object):
    def __init__(self, value):
        self.value = value

    def __get__(self, obj, type=None):
        return self.value

    def __set__(self, obj, val):
        self.value = val

Lihatlah jawaban ini dan dokumen resmi HOWTO untuk informasi lebih lanjut tentang deskriptor.

Yann
sumber
2
Anda juga bisa menggunakan @property, yang sama dengan menggunakan deskriptor, tetapi kode ini jauh lebih sedikit.
Rick mendukung Monica
6

Benar-benar Ya, Python dengan sendirinya tidak memiliki anggota data statis secara eksplisit, tetapi Kami dapat melakukannya dengan melakukannya

class A:
    counter =0
    def callme (self):
        A.counter +=1
    def getcount (self):
        return self.counter  
>>> x=A()
>>> y=A()
>>> print(x.getcount())
>>> print(y.getcount())
>>> x.callme() 
>>> print(x.getcount())
>>> print(y.getcount())

keluaran

0
0
1
1

penjelasan

here object (x) alone increment the counter variable
from 0 to 1 by not object y. But result it as "static counter"
Mari Selvan
sumber
6

Ya, sangat mungkin untuk menulis variabel dan metode statis dengan python.

Variabel Statis: Variabel yang dideklarasikan di tingkat kelas disebut variabel statis yang dapat diakses langsung menggunakan nama kelas.

    >>> class A:
        ...my_var = "shagun"

    >>> print(A.my_var)
        shagun

Variabel Instance: Variabel yang terkait dan diakses oleh instance dari kelas adalah variabel instan.

   >>> a = A()
   >>> a.my_var = "pruthi"
   >>> print(A.my_var,a.my_var)
       shagun pruthi

Metode Statis: Mirip dengan variabel, metode statis dapat diakses secara langsung menggunakan Nama kelas. Tidak perlu membuat instance.

Namun perlu diingat, metode statis tidak dapat memanggil metode non-statis dengan python.

    >>> class A:
   ...     @staticmethod
   ...     def my_static_method():
   ...             print("Yippey!!")
   ... 
   >>> A.my_static_method()
   Yippey!!
Shagun Pruthi
sumber
4

Untuk menghindari kemungkinan kebingungan, saya ingin membandingkan variabel statis dan objek yang tidak dapat diubah.

Beberapa tipe objek primitif seperti integer, float, string, dan touple tidak berubah dalam Python. Ini berarti bahwa objek yang dirujuk oleh nama yang diberikan tidak dapat berubah jika itu salah satu dari jenis objek yang disebutkan di atas. Nama dapat dipindahkan ke objek yang berbeda, tetapi objek itu sendiri tidak dapat diubah.

Membuat variabel statis mengambil langkah ini lebih jauh dengan melarang nama variabel untuk menunjuk ke objek apa pun kecuali yang saat ini ditunjuknya. (Catatan: ini adalah konsep perangkat lunak umum dan tidak khusus untuk Python; silakan lihat posting orang lain untuk informasi tentang penerapan statika dalam Python).

Ross
sumber
4

Cara terbaik yang saya temukan adalah menggunakan kelas lain. Anda dapat membuat objek dan kemudian menggunakannya pada objek lain.

class staticFlag:
    def __init__(self):
        self.__success = False
    def isSuccess(self):
        return self.__success
    def succeed(self):
        self.__success = True

class tryIt:
    def __init__(self, staticFlag):
        self.isSuccess = staticFlag.isSuccess
        self.succeed = staticFlag.succeed

tryArr = []
flag = staticFlag()
for i in range(10):
    tryArr.append(tryIt(flag))
    if i == 5:
        tryArr[i].succeed()
    print tryArr[i].isSuccess()

Dengan contoh di atas, saya membuat kelas bernama staticFlag.

Kelas ini harus menyajikan var statis __success(Private Static Var).

tryIt kelas mewakili kelas reguler yang perlu kita gunakan.

Sekarang saya membuat objek untuk satu flag ( staticFlag). Bendera ini akan dikirim sebagai referensi ke semua objek biasa.

Semua objek ini sedang ditambahkan ke daftar tryArr.


Hasil Skrip Ini:

False
False
False
False
False
True
True
True
True
True
Tomer Zait
sumber
2

Variabel statis di Kelas pabrik python3.6

Bagi siapa pun yang menggunakan pabrik kelas dengan python3.6 dan lebih tinggi gunakan nonlocalkata kunci untuk menambahkannya ke lingkup / konteks kelas yang sedang dibuat seperti:

>>> def SomeFactory(some_var=None):
...     class SomeClass(object):
...         nonlocal some_var
...         def print():
...             print(some_var)
...     return SomeClass
... 
>>> SomeFactory(some_var="hello world").print()
hello world
jmunsch
sumber
ya, tetapi dalam hal ini hasattr(SomeClass, 'x')adalah False. saya ragu ini yang orang maksud dengan variabel statis sama sekali.
Rick mendukung Monica
@ RickTeachey lol, melihat kode variabel statis Anda, stackoverflow.com/a/27568860/2026508 +1 Pak internet, dan saya pikir hasattr tidak berfungsi seperti itu? jadi tidak some_varberubah, dan didefinisikan secara statis, atau bukan? Apa yang harus dilakukan oleh akses pengambil luar dengan variabel yang statis atau tidak? Saya punya banyak pertanyaan sekarang. akan senang mendengar beberapa jawaban ketika Anda mendapatkan waktu.
jmunsch
Ya metaclass itu cukup konyol. Saya tidak yakin saya mengerti pertanyaan-pertanyaan tetapi untuk pikiran saya, di some_varatas bukan anggota kelas sama sekali. Dalam Python semua anggota kelas dapat diakses dari luar kelas.
Rick mendukung Monica
The nonlocalkeywoard "benjolan" ruang lingkup variabel. Lingkup definisi badan kelas tidak tergantung pada ruang lingkup yang ditemukannya sendiri ketika Anda mengatakan nonlocal some_var, itu hanya membuat referensi nama non-lokal (baca: BUKAN dalam lingkup definisi kelas) ke objek bernama lain. Oleh karena itu tidak melekat pada definisi kelas karena tidak ada dalam ruang lingkup kelas.
Rick mendukung Monica
1

Jadi ini mungkin hack, tapi saya sudah menggunakan eval(str)untuk mendapatkan objek statis, semacam kontradiksi, dengan python 3.

Ada file Records.py yang hanya memiliki classobjek yang ditentukan dengan metode statis dan konstruktor yang menyimpan beberapa argumen. Kemudian dari file .py lain Iimport Records tetapi saya perlu secara dinamis memilih setiap objek dan kemudian instantiate sesuai permintaan sesuai dengan jenis data yang sedang dibaca.

Jadi di mana object_name = 'RecordOne'atau nama kelas, saya panggil cur_type = eval(object_name)dan kemudian untuk instantiate Anda lakukan cur_inst = cur_type(args) Namun sebelum Anda instantiate Anda dapat memanggil metode statis dari cur_type.getName()misalnya, jenis seperti implementasi kelas dasar abstrak atau apa pun tujuannya. Namun di backend, mungkin instantiated dengan python dan tidak benar-benar statis, karena eval mengembalikan objek .... yang pasti instantiated .... yang memberikan perilaku seperti statis.

Christopher Hoffman
sumber
0

Anda dapat menggunakan daftar atau kamus untuk mendapatkan "perilaku statis" di antara instance.

class Fud:

     class_vars = {'origin_open':False}

     def __init__(self, origin = True):
         self.origin = origin
         self.opened = True
         if origin:
             self.class_vars['origin_open'] = True


     def make_another_fud(self):
         ''' Generating another Fud() from the origin instance '''

         return Fud(False)


     def close(self):
         self.opened = False
         if self.origin:
             self.class_vars['origin_open'] = False


fud1 = Fud()
fud2 = fud1.make_another_fud()

print (f"is this the original fud: {fud2.origin}")
print (f"is the original fud open: {fud2.class_vars['origin_open']}")
# is this the original fud: False
# is the original fud open: True

fud1.close()

print (f"is the original fud open: {fud2.class_vars['origin_open']}")
# is the original fud open: False
Jay
sumber
0

Jika Anda mencoba untuk membagikan variabel statis untuk, dengan contoh, meningkatkannya di contoh lain, sesuatu seperti skrip ini berfungsi dengan baik:

# -*- coding: utf-8 -*-
class Worker:
    id = 1

    def __init__(self):
        self.name = ''
        self.document = ''
        self.id = Worker.id
        Worker.id += 1

    def __str__(self):
        return u"{}.- {} {}".format(self.id, self.name, self.document).encode('utf8')


class Workers:
    def __init__(self):
        self.list = []

    def add(self, name, doc):
        worker = Worker()
        worker.name = name
        worker.document = doc
        self.list.append(worker)


if __name__ == "__main__":
    workers = Workers()
    for item in (('Fiona', '0009898'), ('Maria', '66328191'), ("Sandra", '2342184'), ('Elvira', '425872')):
        workers.add(item[0], item[1])
    for worker in workers.list:
        print(worker)
    print("next id: %i" % Worker.id)
Pasukan Musim Dingin
sumber