Python'da statik sınıf değişkenleri mümkün müdür?


Yanıtlar:


1900

Sınıf tanımının içinde bildirilen ancak bir yöntem içinde belirtilmeyen değişkenler sınıf veya statik değişkenlerdir:

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

@ Millerdev'in işaret ettiği gibi , bu, sınıf düzeyinde bir ideğişken oluşturur, ancak bu, herhangi bir örnek düzeyi ideğişkeninden farklıdır , bu nedenle

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

Bu, C ++ ve Java'dan farklıdır, ancak bir örneğe başvuru kullanılarak statik bir üyeye erişilemediği C # 'dan çok farklı değildir.

Sınıflar ve sınıf nesneleri konusunda Python öğreticisinin neler söylediğine bakın .

@Steve Johnson, Python Kütüphanesi Referansında "Yerleşik İşlevler" altında da belgelenen statik yöntemlerle ilgili olarak zaten yanıt verdi .

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

@beidy , yöntem daha sonra sınıf argümanını ilk argüman olarak aldığından, staticmethod üzerinden classmethod'ları önerir , ancak hala staticmethod'a göre bu yaklaşımın avantajları üzerinde biraz bulanıkım. Siz de iseniz, muhtemelen önemli değil.


11
Ben sadece Python öğreniyorum, ama AFAIK @classmethodüzerinde avantajları @staticmethodher zaman bir alt sınıf olsa bile, yöntemin çağrıldığı sınıfın adını almak olmasıdır. Statik bir yöntemde bu bilgi yoktur, bu nedenle örneğin geçersiz kılınmış bir yöntem çağıramaz.
Seb

49
@theJollySin sabitler için pythonic yol sabitler için bir sınıf büyümemektir. Sadece bazılarınız const.pyvar PI = 3.14ve her yere aktarabilirsiniz. from const import PI
Giszmo

30
Bu yanıtın statik değişken sorununu karıştırması muhtemeldir. Öncelikle, i = 3bir değil bir sınıf niteliği olduğunu ve bir örnek düzeyi özniteliği ayrı olmasından dolayı, statik bir değişken io mu değil diğer dillerde statik değişken gibi davranır. Bkz millerdev yanıtını , Yann'ın cevabı ve benim cevap aşağıda.
Rick,

2
iBu sınıfın yüzlerce örneğini oluştursam bile (statik değişken) yalnızca bir kopyası bellekte olacak?
sdream

2
Daniel'in @Dubslow yorumunda kimin bahsettiğini merak edenler için, millerdev (geri dönüş makinesi )
HeyJude

618

@Blair Conrad, sınıf tanımında bildirilen ancak bir yöntem içinde belirtilmeyen statik değişkenlerin sınıf veya "statik" değişkenler olduğunu söyledi:

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

Burada birkaç tane yakaladım. Yukarıdaki örnekten devam etmek:

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

t.iÖznitelik idoğrudan ayarlandığında örnek değişkeninin "statik" sınıf değişkeni ile nasıl senkronize olmadığına dikkat edin t. Bunun nedeni i, tad alanından farklı olan ad alanı içinde yeniden bağlanmış olmasıdır Test. "Statik" bir değişkenin değerini değiştirmek istiyorsanız, değişkeni orijinal olarak tanımlandığı kapsam (veya nesne) içinde değiştirmeniz gerekir. Ben "statik" tırnak koymak çünkü Python gerçekten C ++ ve Java yapmak anlamında statik değişkenler yok.

Statik değişkenler veya yöntemler hakkında spesifik bir şey söylemese de, Python öğreticisinde sınıflar ve sınıf nesneleri ile ilgili bazı bilgiler bulunur .

@Steve Johnson, Python Kütüphanesi Referansında "Yerleşik İşlevler" altında da belgelenen statik yöntemlerle ilgili olarak cevap verdi.

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

@, aynı zamanda, statik metoda benzer sınıf metodundan da bahsetmiştir. Bir sınıf yönteminin ilk argümanı sınıf nesnesidir. Misal:

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

Yukarıdaki Örneğin Resimli Temsili


3
Örneği biraz genişletmenizi öneririm: Test.i = 6 ayarlandıktan sonra yeni bir nesne (örn. U = Test ()) oluşturduysanız, yeni nesne yeni sınıf değerini (örneğin, ui == 6)
Mark

2
Senkronize statik değişkenleri tutmak için bir yol onları özelliklerini yapmaktır: class Test(object):, _i = 3, @property, def i(self), return type(self)._i, @i.setter, def i(self,val):, type(self)._i = val. Şimdi yapabilirsiniz x = Test(), x.i = 12, assert x.i == Test.i.
Rick,

1
Yani tüm değişkenler başlangıçta statik olduğunu söyleyebilir ve sonra örneklere erişmek zamanında çalışma değişkenleri yapar?
Ali

Belki de ilginçtir: Test'te Test.i'yi değiştiren bir yöntem tanımlarsanız, BOTH Test.i ve ti değerlerini etkiler.
Pablo

@millerdev, u bahsettiğimiz gibi Python C ++ veya JAVA gibi statik değişkenlere sahip değil ... Yani, Test.i statik bir değişken yerine bir sınıf değişkeninden daha mı?
Tyto

197

Statik ve Sınıf Yöntemleri

Diğer cevapların belirttiği gibi, statik ve sınıf yöntemleri yerleşik dekoratörler kullanılarak kolayca gerçekleştirilir:

class Test(object):

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

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

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

Her zamanki gibi, ilk argüman MyMethod()sınıf örneği nesnesine bağlıdır. Bunun aksine, ilk değişken için MyClassMethod()olan sınıf nesnesine bağlı (bu durumda, örneğin Test). Çünkü MyStaticMethod(), argümanların hiçbiri bağlı değildir ve hiçbir argümana sahip olmak isteğe bağlıdır.

"Statik Değişkenler"

Ancak, "statik değişkenler" (iyi, değişebilir statik değişkenler, her neyse, eğer bu bir çelişki değilse ...) uygulamak o kadar da ileri değildir. Millerdev'in cevabında belirttiği gibi , sorun Python'un sınıf özelliklerinin gerçekten "statik değişkenler" olmamasıdır. Düşünmek:

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

Çizgi Bunun nedeni, x.i = 12yeni bir örnek özelliğini eklemiştir iiçin xyerine değerinin değiştirilmesi Testsınıfı iöznitelik.

Kısmi beklenen statik değişken davranışı, yani (ama birden çok örneği arasında özelliğinin senkronize olmayan sınıfın kendisi ile; "yakaladım" bkz aşağıda), bir mülkte class özelliği çevirerek elde edilebilir:

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)

Şimdi şunları yapabilirsiniz:

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

Statik değişken artık tüm sınıf örnekleri arasında senkronize kalacaktır .

(NOT: Yani, bir sınıf örneği kendi sürümünü tanımlamaya karar vermedikçe _i! Ama birisi bunu yapmaya karar verirse, aldıklarını hak ederler, değil mi ???)

Teknik olarak konuşmanın ihala bir 'statik değişken' olmadığını unutmayın ; Bir olan propertytanımlayıcısı özel bir türü olan. Ancak, propertydavranış şimdi tüm sınıf örnekleri arasında eşitlenmiş (değişken) statik değişkene eşdeğerdir.

Değişmez "Statik Değişkenler"

Değişmez statik değişken davranış için propertyayarlayıcıyı atlayın :

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)

Şimdi örnek initeliğini ayarlamaya çalışmak aşağıdakileri döndürür AttributeError:

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

Farkına Varmak İçin Bir Gotcha

Yukarıdaki yöntemler ile sadece iş o Not örnekleri bunlar olacak - sınıfınızın değil işin sınıfını kendisi kullanarak . Yani mesela:

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'

Çizgi assert Test.i == x.iiçin, bir hata üretir initeliği Testve xiki farklı nesneleridir.

Birçok insan bunu şaşırtıcı bulacaktır. Ancak, olmamalı. Geri dönüp Testsınıf tanımımızı (ikinci sürüm) incelersek, bu satırı not ederiz:

    i = property(get_i) 

Açıktır ki, eleman iarasında Testmutlaka bir olmak propertydönen nesne türü bir amacı, propertyfonksiyon.

Yukarıda kafa karıştırıcı bulursanız, büyük olasılıkla hala diğer diller açısından (örneğin Java veya c ++) düşünüyorsunuz. Sen incelemek gitmeli propertyPython nitelikleri iade sırayla, açıklayıcısı protokolü ve yöntem çözünürlüğü sırayla (MRO) hakkında nesne.

Ben aşağıda yukarıdaki 'gotcha' için bir çözüm sunuyoruz; ancak - yorucu bir şekilde - en azından assert Test.i = x.ineden bir hataya neden olduğunu anlayana kadar aşağıdaki gibi bir şey yapmaya çalışmamanızı öneririm .

GERÇEK, GERÇEK Statik Değişkenler -Test.i == x.i

Aşağıdaki (Python 3) çözümünü yalnızca bilgilendirme amacıyla sunuyorum. Ben bunu "iyi bir çözüm" olarak kabul etmiyorum. Python'daki diğer dillerin statik değişken davranışlarını taklit etmenin gerçekten gerekli olup olmadığı konusunda şüphelerim var. Bununla birlikte, gerçekten yararlı olup olmadığına bakılmaksızın, aşağıdakiler Python'un nasıl çalıştığını daha iyi anlamaya yardımcı olmalıdır.

GÜNCELLEME: bu girişim gerçekten çok korkunç ; eğer böyle bir şey yapmakta ısrar ediyorsanız (ipucu: lütfen yapma; Python çok zarif bir dildir ve ayakkabı dilini başka bir dil gibi davranmak zorunda değildir), bunun yerine Ethan Furman'ın cevabındaki kodu kullanın.

Metasınıf kullanarak diğer dillerin statik değişken davranışlarını taklit etme

Metasınıf bir sınıfın sınıfıdır. Python'daki tüm sınıflar için varsayılan metasınıf (yani "yeni stil" sınıfları Python 2.3 sonrası inanıyorum) type. Örneğin:

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

Ancak, kendi metasınıfınızı şu şekilde tanımlayabilirsiniz:

class MyMeta(type): pass

Ve bunu kendi sınıfınıza uygulayın (yalnızca Python 3):

class MyClass(metaclass = MyMeta):
    pass

type(MyClass)  # class MyMeta

Aşağıda, diğer dillerin "statik değişken" davranışını taklit etmeye çalışan bir metasınıf var. Temel olarak, varsayılan alıcı, ayarlayıcı ve silmeyi, istenen özniteliğin "statik değişken" olup olmadığını kontrol eden sürümlerle değiştirerek çalışır.

"Statik değişkenler" kataloğu StaticVarMeta.staticsöznitelikte saklanır . Tüm öznitelik istekleri başlangıçta yedek çözüm siparişi kullanılarak çözümlenmeye çalışılır. Bunu "statik çözünürlük sırası" veya "SRO" olarak adlandırdım. Bu, belirli bir sınıf (veya üst sınıfları) için "statik değişkenler" kümesinde istenen öznitelik aranarak yapılır. Öznitelik "SRO" da görünmezse, sınıf varsayılan öznitelik alma / ayarlama / silme davranışına (yani "MRO") geri döner.

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

Yolunuzu kullanmaya çalıştım ama bir sorunla karşılaştım, lütfen soruma bir göz atın burada stackoverflow.com/questions/29329850/get-static-variable-value
Muhammed Refaat

@RickTeachey: Sanırım genellikle Instance sınıfında Test(örnekleme örnekleri için kullanmadan önce) yaptığınız her şeyi meta-programlama alanında görmelisiniz ? Örneğin, yaparak sınıf davranışını değiştirirsiniz Test.i = 0(burada özellik nesnesini tamamen yok edersiniz). "Özellik-mekanizma" sadece bir sınıf örnekleri üzerinde özellik erişimi başladı (belki de bir meta-sınıf ara olarak kullanarak temel davranış değiştirmek sürece) sanırım. Btw, lütfen bu cevabı bitir :-)
Ole Thomsen Buus

1
@RickTeachey Teşekkürler :-) Sonunda metaclass ilginç ama aslında benim sevme için biraz fazla karmaşık. Bu mekanizmanın kesinlikle gerekli olduğu geniş bir çerçeve / uygulamada faydalı olabilir. Her neyse, bu, yeni (karmaşık) varsayılan olmayan meta-davranışa gerçekten ihtiyaç duyulursa, Python'un bunu mümkün kıldığını gösterir :)
Ole Thomsen Buus

1
@OleThomsenBuus: İşimi yapan daha basit bir metasınıf için cevabımı kontrol et .
Ethan Furman

1
@taper Haklısınız; Sorunu düzeltmek için cevabı düzenledim (çok uzun süre yanlış oturduğuna inanamıyorum!). Karışıklık için özür dilerim.
Rick, Monica

33

Ayrıca sınıflara anında değişkenler ekleyebilirsiniz

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

Ve sınıf örnekleri sınıf değişkenlerini değiştirebilir

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

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

>python test.py
[1]
[1, 1]

3
Sınıf başka bir modüle alınsa bile yeni sınıf değişkenleri yapışır mı?
zakdances

Evet. Sınıflar, adını verdiğiniz ad alanına bakılmaksızın etkili bir şekilde tekil öğedir.
Pedro

@Gregory, "Ve sınıf örnekleri sınıf değişkenlerini değiştirebilir" dediniz Aslında bu örneğe erişim değil değişiklik denir. Değişiklik, nesnenin kendisi tarafından kendi append () işlevi aracılığıyla yapıldı.
Amr ALHOSSARY

19

Statik bir yönteme ihtiyaç duyduğumda şahsen bir sınıf yöntemi kullanırdım. Temelde sınıfı bir argüman olarak aldığım için.

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

veya bir dekoratör kullanın

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

Statik özellikler için .. Onun zaman bazı python tanımı ararken .. değişken her zaman değişebilir. Değişken ve değişmez iki türü vardır .. Ayrıca, sınıf öznitelikleri ve örnek öznitelikleri vardır .. Hiçbir şey gerçekten java & c ++ anlamında statik öznitelikler gibi değil

Sınıfla herhangi bir ilişkisi yoksa neden statik yöntemi pythonic anlamında kullanın! Ben olsaydım ya classmethod kullanırdım ya da sınıftan bağımsız bir yöntem tanımlardım.


1
Değişkenler değişmez veya değişmez değildir; nesneler. (Ancak, bir nesne, değişen derecelerde başarı ile, bazı özelliklerine
Davis Herring

Java ve C ++, örneğe karşı sınıf özniteliğini kullandığınız gibi statik (imho kelimesinin kötü kullanımı, imho) sözcüğünü kullanır. Bir sınıf özniteliği / yöntemi Java ve C ++ 'da statiktir, fark yoktur, ancak Python'da bir sınıf yöntemi çağrısının ilk parametresi sınıftır.
Angel O'Sphere

16

Aşağıdaki örnekte gösterilen statik özellikler ve örnek özellikler hakkında not edilmesi gereken özel bir şey:

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

Bu, örnek örneği özelliğine atamadan önce, örneğe özelliğe erişmeye çalışırsak statik değer kullanılır. Python sınıfında bildirilen her özelliğin her zaman bellekte statik bir yuvası vardır .


16

Pitondaki statik yöntemlere classmethod s denir . Aşağıdaki koda bir göz atın

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

MyInstanceMethod yöntemini çağırdığımızda bir hata aldığımızı göreceksiniz . Bunun nedeni, bu yöntemin bu sınıfın bir örneğinde çağrılmasını gerektirmesidir. MyStaticMethod yöntemi , dekoratör @classmethod kullanılarak bir sınıf yöntemi olarak ayarlanır .

Sadece tekmeler ve kıkırdamalar için, sınıfın bir örneğini ileterek sınıfa myInstanceMethod diyebiliriz :

>>> MyClass.myInstanceMethod(MyClass())
output from an instance method

2
Umm ... statik yöntemlerle @staticmethod; @classmethod(açıkçası) sınıf yöntemleri içindir (bunlar esas olarak alternatif kurucular olarak kullanılmak üzere tasarlanmıştır, ancak bir çimdik içinde çağrıldıkları sınıfa referans alan statik yöntemler olarak hizmet edebilir).
ShadowRanger

11

Herhangi bir üye yönteminin dışında bir üye değişken tanımlandığında, değişkenin nasıl ifade edildiğine bağlı olarak değişken statik veya statik olmayabilir.

  • CLASSNAME.var statik değişkendir
  • INSTANCENAME.var statik değişken değil.
  • sınıf içindeki self.var statik değişken değildir.
  • sınıf üyesi içindeki var işlevi tanımlanmamış.

Örneğin:

#!/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()

Sonuçlar

self.var is 2
A.var is 1
self.var is 2
A.var is 3

Girinti kırıldı. Bu gerçekleşmeyecek
Thomas Weller

9

staticSınıf değişkenlerine sahip olmak mümkündür , ama muhtemelen çabaya değmez.

İşte Python 3'te yazılmış bir kavram kanıtı - kesin detaylardan herhangi biri yanlışsa, kod, ne demek istediğinizle hemen hemen eşleşecek şekilde değiştirilebilir 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)

ve kullanımda:

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

ve bazı testler:

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

8

Ayrıca, bir sınıfı metasınıf kullanarak statik olması için zorlayabilirsiniz.

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

Ne zaman yanlışlıkla MyClass başlatmaya çalıştığınızda bir StaticClassError alırsınız.


4
Neden örnek vermeyecekseniz neden bir sınıf bile? Bu, Python'u Java'ya dönüştürmek için bükmek gibi geliyor ....
Ned Batchelder

1
Borg deyim bu işlemek için daha iyi bir yoldur.
Rick, Monica

@NedBatchelder Sadece alt sınıflar (ve alt sınıfları örnekleme) için tasarlanmış soyut bir sınıftır
stevepastelan

1
Umarım alt sınıflar __new__ebeveynlerini çağırmak için super () kullanmazlar ...
Ned Batchelder

7

Python'un öznitelik aramasıyla ilgili çok ilginç bir nokta, " sanal değişkenler" oluşturmak için kullanılabilmesidir :

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!

Normalde, oluşturulduktan sonra bunlara herhangi bir atama yapılmaz. Belirli bir örnekle ilişkilendirilememesi anlamında statik selfolsa labelda , değerin yine de (sınıfının) örneğine bağlı olduğu için aramanın kullandığını unutmayın .


6

Bu cevapla ilgili olarak , sabit bir statik değişken için bir tanımlayıcı kullanabilirsiniz. İşte bir örnek:

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

sonuçlanan ...

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

Ayar değerini ( passyukarıda) sessizce göz ardı etmek sizin işiniz değilse her zaman bir istisna oluşturabilirsiniz . Bir C ++, Java stili statik sınıf değişkeni arıyorsanız:

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

Göz at Bu cevap ve resmi belgeler NASIL tanımlayıcıları hakkında daha fazla bilgi için.


2
Ayrıca sadece @propertybir tanımlayıcı kullanmakla aynı olan kullanabilirsiniz , ancak çok daha az kod.
Rick,

6

Kesinlikle Evet, Python'un kendi başına herhangi bir statik veri üyesi yoktur, ancak bunu yapabiliriz.

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

çıktı

0
0
1
1

açıklama

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

6

Evet, python'a statik değişkenler ve yöntemler yazmak kesinlikle mümkündür.

Statik Değişkenler: Sınıf düzeyinde bildirilen değişkene, sınıf adı kullanılarak doğrudan erişilebilen statik değişken denir.

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

    >>> print(A.my_var)
        shagun

Örnek değişkenleri: Bir sınıf örneğiyle ilişkilendirilen ve erişilen değişkenler örnek değişkenleridir.

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

Statik Yöntemler: Değişkenlere benzer şekilde, statik yöntemlere doğrudan Sınıf Adı kullanılarak erişilebilir. Bir örnek oluşturmaya gerek yok.

Ancak unutmayın, statik bir yöntem python'da statik olmayan bir yöntemi çağıramaz.

    >>> class A:
   ...     @staticmethod
   ...     def my_static_method():
   ...             print("Yippey!!")
   ... 
   >>> A.my_static_method()
   Yippey!!

4

Herhangi bir olası karışıklığı önlemek için, statik değişkenleri ve değişmez nesneleri karşılaştırmak istiyorum.

Tamsayılar, şamandıralar, dizgiler ve başlıklar gibi bazı ilkel nesne türleri Python'da değişmez. Bu, belirli bir adla atıfta bulunulan nesnenin, yukarıda belirtilen nesne türlerinden biriyse değişemeyeceği anlamına gelir. Ad farklı bir nesneye yeniden atanabilir, ancak nesnenin kendisi değiştirilemeyebilir.

Değişken statik yapmak, değişken isminin şu anda işaret ettiği herhangi bir nesneyi işaret etmesine izin vermeyerek bunu bir adım öteye taşır. (Not: Bu genel bir yazılım konseptidir ve Python'a özgü değildir; Python'da statik uygulama hakkında bilgi için lütfen başkalarının gönderilerine bakın).


4

Bulduğum en iyi yol başka bir sınıf kullanmak. Bir nesne oluşturabilir ve daha sonra diğer nesnelerde kullanabilirsiniz.

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

Yukarıdaki örnekle, adlı bir sınıf yaptım staticFlag.

Bu sınıf statik var __success(Özel Statik Var) göstermelidir.

tryIt sınıf, kullanmamız gereken normal sınıfı temsil ediyordu.

Şimdi bir bayrak ( staticFlag) için bir nesne yaptım . Bu bayrak tüm normal nesnelere referans olarak gönderilecektir.

Tüm bu nesneler listeye ekleniyor tryArr.


Bu Script Sonuçları:

False
False
False
False
False
True
True
True
True
True

2

Sınıf fabrikasında statik değişkenler python3.6

Python3.6 ve üstü bir sınıf fabrikası kullanan herkes için nonlocalanahtar kelimeyi kullanarak, oluşturulan sınıfın kapsamına / bağlamına aşağıdaki gibi ekleyin:

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

evet, ama bu durumda hasattr(SomeClass, 'x')olduğunu False. Herkes bu statik bir değişken tarafından ne anlama geliyor şüpheliyim.
Rick,

@RickTeachey lol, statik değişken kodunuzu gördünüz, stackoverflow.com/a/27568860/2026508 +1 internet efendim ve hasattr'ın böyle çalışmadığını düşündüm? böyledir some_var, değişmez ve statik olarak tanımlanmış, yoksa değil mi? Dış alıcı erişiminin bir değişkenin statik olması ya da olmaması ile ne ilgisi var? Şimdi çok fazla sorum var. zaman geldiğinde bazı cevapları duymak isteriz.
jmunsch

Evet, metasınıf oldukça saçma. Soruları anladığımdan emin değilim ama aklımda, some_varyukarıdaki hiç bir sınıf üyesi değil. Python'da tüm sınıf üyelerine sınıfın dışından erişilebilir.
Rick,

nonlocalKeywoard değişken kapsamı "tümsekler". Sınıf gövdesi tanımının kapsamı nonlocal some_var, yalnızca başka bir adlandırılmış nesneye yerel olmayan (okuma: sınıf tanımı kapsamında DEĞİL) ad başvurusu oluştururken kendi içinde bulduğu kapsamdan bağımsızdır . Bu nedenle, sınıf tanımına eklenmez, çünkü sınıf gövdesi kapsamında değildir.
Rick,

1

Bu muhtemelen bir hack, ama ben eval(str)piton 3'te statik bir nesne, biraz çelişki elde etmek için kullanıyorum .

classBazı bağımsız değişkenleri kaydeden statik yöntemlerle ve yapıcılarla tanımlanan nesnelerden başka hiçbir şeye sahip olmayan bir Records.py dosyası vardır . Sonra başka bir .py dosyasından ben import Recordsama dinamik olarak her nesneyi seçmek ve daha sonra okunan veri türüne göre istek üzerine somutlaştırmak gerekir.

Yani nerede object_name = 'RecordOne'veya sınıf adı, ben çağırmak cur_type = eval(object_name)ve sonra bunu örneklemek için cur_inst = cur_type(args) Ancak , örnekleme önce statik cur_type.getName()temelleri örneğin, soyut temel sınıf uygulaması gibi veya hedef ne olursa olsun çağırabilirsiniz . Ancak arka uçta, muhtemelen python içinde somutlaştırılır ve gerçekten statik değildir, çünkü eval bir nesne döndürüyor .... bu somutlaştırılmış olması gerekir .... statik benzeri davranış verir.


0

Örnekler arasında "statik davranış" elde etmek için bir liste veya sözlük kullanabilirsiniz.

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

0

Örneğin, diğer örneklerde artırmak için statik bir değişkeni paylaşmaya çalışıyorsanız, bu komut dosyası gibi bir şey işe yarar:

# -*- 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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.