Чи можливі змінні статичного класу в Python?


Відповіді:


1900

Змінні, оголошені всередині визначення класу, але не всередині методу, є змінними класу або статичними:

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

Як зазначає @ millerdev , це створює iзмінну рівня класу , але це відрізняється від будь-якої iзмінної рівня екземпляра , тому ви могли мати

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

Це відрізняється від C ++ та Java, але не настільки відрізняється від C #, де статичний член не може отримати доступ, використовуючи посилання на екземпляр.

Подивіться, що підручник Python має сказати на предмет класів та об’єктів класу .

@Steve Johnson вже відповів про статичні методи , також задокументовані в розділі "Вбудовані функції" в бібліотеці Python Reference .

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

@beidy рекомендує classmethod s над staticmethod, оскільки метод отримує тип класу в якості першого аргументу, але я все ще трохи нечіткий щодо переваг цього підходу перед статичним методом. Якщо ти теж, то, мабуть, це не має значення.


11
Я просто вивчаю Python, але переваги @classmethodнад @staticmethodAFAIK полягають у тому, що ви завжди отримуєте ім'я класу, на який посилався метод, навіть якщо це підклас. Статичному методу не вистачає цієї інформації, тому він не може викликати переохоплений метод, наприклад.
Себ

49
@theJollySin пітонічним способом для констант є не вирощувати клас для констант. Просто деякі const.pyз , PI = 3.14і ви можете імпортувати його всюди. from const import PI
Giszmo

30
Ця відповідь, ймовірно, бентежить проблему статичної змінної. Почнемо з того , i = 3це НЕ змінної статична, вона є атрибутом класу, і так як він відрізняється від атрибута примірника рівня iвін НЕ поводиться як статичні змінні в інших мовах. Див відповідь millerdev в , відповідь Yann, і моя відповідь нижче.
Рік підтримує Моніку

2
тож лише одна копія i(статична змінна) буде в пам'яті, навіть якщо я створять сотні екземплярів цього класу?
sdream

2
Для всіх, хто цікавиться, кого згадав Даніель у коментарі @Dubslow, це millerdev ( зворотний автомат )
HeyJude,

618

@Blair Conrad зазначив, що статичні змінні, оголошені всередині визначення класу, але не всередині методу, є змінними класу або "статичні":

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

Тут є кілька готчей. Продовжуючи з наведеного вище прикладу:

>>> 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вийшла із синхронізації зі змінною класу "статична", коли атрибут iбув встановлений безпосередньо на t. Це відбувається тому, що він iбув повторно пов'язаний у tпросторі імен, що відрізняється від Testпростору імен. Якщо ви хочете змінити значення "статичної" змінної, ви повинні змінити її в межах (або об'єкта), де воно було визначено спочатку. Я ставлю "статичні" в лапки, тому що Python насправді не має статичних змінних у тому сенсі, як це роблять C ++ та Java.

Хоча в ньому не сказано нічого конкретного щодо статичних змінних чи методів, у підручнику Python є відповідна інформація про класи та об’єкти класу .

@Steve Johnson також відповів щодо статичних методів, також задокументованих у розділі "Вбудовані функції" у бібліотеці Python Reference.

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

@beid також згадав classmethod, який схожий на staticmethod. Перший аргумент classmethod - це об'єкт класу. Приклад:

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

Образотворче зображення вищевказаного прикладу


3
Я пропоную вам трохи розширити приклад: якщо після встановлення Test.i = 6 ви інстанціюєте новий об'єкт (наприклад, u = Test ()), новий об'єкт "успадкує" нове значення класу (наприклад, ui == 6)
Марк

2
Спосіб зберегти статичні змінні в синхронізації, щоб зробити їх властивості: class Test(object):, _i = 3, @property, def i(self), return type(self)._i, @i.setter, def i(self,val):, type(self)._i = val. Тепер ви можете зробити x = Test(), x.i = 12, assert x.i == Test.i.
Рік підтримує Моніку

1
Тож я можу сказати, що всі змінні спочатку статичні, а потім доступ до екземплярів робить змінні екземпляри під час виконання?
Алі

Можливо, це цікаво: якщо ви визначите метод у тесті, який змінює Test.i, це вплине на значення BOTH Test.i та ti.
Пабло

@millerdev, як і у згаданого Python, немає статичних змінних, як у C ++ або JAVA. Так буде добре сказати, Test.i - це більше змінна класу, а не статична змінна?
Тайто

197

Статичні та класичні методи

Як зазначалося в інших відповідях, статичні та класичні методи легко виконуються за допомогою вбудованих декораторів:

class Test(object):

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

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

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

Як завжди, перший аргумент до MyMethod()прив'язується до об'єкта екземпляра класу. На противагу цьому , перший аргумент MyClassMethod()буде пов'язаний з самим об'єктом класу (наприклад, в даному випадку Test). Бо MyStaticMethod()жоден аргумент не пов'язаний, і аргументи взагалі не є обов'язковими.

"Статичні змінні"

Однак реалізація "статичних змінних" (ну, мінливих статичних змінних, у будь-якому випадку, якщо це не є суперечливістю в термінах ...) не настільки прямо. Як зазначив у своїй відповіді Міллердев , проблема полягає в тому, що атрибути класу Python не є справді "статичними змінними". Поміркуйте:

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

Це відбувається тому , що лінія x.i = 12доданий новий атрибут реалізації , iщоб xзамість зміни значення Testкласу iатрибута.

Часткове очікуване поведінка статичної змінної, тобто синхронізація атрибута між декількома екземплярами (але не з самим класом; див. "Gotcha" нижче), може бути досягнута, перетворивши атрибут класу у властивість:

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)

Тепер ви можете зробити:

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

Статична змінна тепер залишатиметься синхронізованою між усіма екземплярами класу .

(ПРИМІТКА. Тобто, якщо екземпляр класу не вирішить визначити свою власну версію _i! Але якщо хтось вирішить зробити ТОМ, вони заслуговують на те, що отримують, чи не ???)

Зауважте, що технічно кажучи, iце все ще не є "статичною змінною" взагалі; це - propertyспецифічний тип дескриптора. Однак propertyповедінка зараз еквівалентна (мутаційній) статичній змінній, синхронізованій у всіх екземплярах класу.

Невідмінні "Статичні змінні"

Для незмінної поведінки статичної змінної просто опустіть propertyсетер:

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)

Тепер при спробі встановити iатрибут примірника повернеться AttributeError:

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

Один повинен бути обізнаний

Зауважте, що вищезазначені методи працюють лише з екземплярами вашого класу - вони не працюватимуть при використанні самого класу . Так, наприклад:

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'

Рядок assert Test.i == x.iстворює помилку, оскільки iатрибут Testі xє двома різними об'єктами.

Багато людей вважають це дивним. Однак цього бути не повинно. Якщо ми повернемось назад і вивчимо Testвизначення нашого класу (друга версія), ми врахуємо цей рядок:

    i = property(get_i) 

Очевидно, що елемент iз Testповинна бути propertyоб'єктом, який є тип об'єкта , що повертається з propertyфункції.

Якщо ви вважаєте, що вищезгадане заплутане, ви, швидше за все, все ще думаєте про це з точки зору інших мов (наприклад, Java або c ++). Вам слід вивчити propertyоб'єкт, про порядок повернення атрибутів Python, протокол дескриптора та порядок роздільної здатності методу (MRO).

Я представляю рішення вищезгаданої 'gotcha' нижче; однак я б наголосив - наполегливо - щоб ви не намагалися зробити щось подібне до того моменту, поки, як мінімум, - ви досконало не зрозумієте, чому assert Test.i = x.iвикликає помилку.

РЕАЛЬНІ, АКТУАЛЬНІ Статичні змінні -Test.i == x.i

Я представляю рішення (Python 3) нижче для інформаційних цілей. Я не схвалюю це як "гарне рішення". У мене є сумніви в тому, чи насправді необхідна емуляція поведінки статичних змінних інших мов на Python. Однак, незалежно від того, чи є він насправді корисним, наведене нижче має допомогти в подальшому розумінні того, як працює Python.

ОНОВЛЕННЯ: ця спроба дійсно дуже жахлива ; якщо ви наполягаєте на тому, щоб робити щось подібне (натяк: будь ласка, не варто; Python - це дуже елегантна мова, і взуття, яка веде себе так, як інша мова, просто не потрібна), використовуйте код замість відповіді Ітана Фурмана .

Емуляція статичної змінної поведінки інших мов за допомогою метакласу

Метаклас - це клас класу. Метаклас за замовчуванням для всіх класів в Python (тобто, класи "нового стилю" після Python 2.3, я вважаю) є type. Наприклад:

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

Однак ви можете визначити власний метаклас так:

class MyMeta(type): pass

І застосуйте його до власного класу на зразок цього (лише для Python 3):

class MyClass(metaclass = MyMeta):
    pass

type(MyClass)  # class MyMeta

Нижче - створений мною метаклас, який намагається імітувати поведінку "статичної змінної" інших мов. В основному це працює, замінюючи getter, setter та deleter за замовчуванням на версії, які перевіряють, чи запитуваний атрибут є "статичною змінною".

Каталог "статичних змінних" зберігається в StaticVarMeta.staticsатрибуті. Усі запити атрибутів спочатку намагаються вирішити за допомогою порядку заміщення рішення. Я назвав це "наказом статичної роздільної здатності", або "СРО". Це робиться шляхом пошуку запитуваного атрибута в наборі "статичних змінних" для даного класу (або його батьківських класів). Якщо атрибут не відображається в "SRO", клас повернеться до атрибуту за замовчуванням поведінка get / set / delete (тобто "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

Я намагався використовувати свій шлях , але я зіткнувся з проблемою, будь ласка, подивіться на моє запитання тут stackoverflow.com/questions/29329850/get-static-variable-value
Мухаммадом Refaat

@RickTeachey: Я думаю, ви, як правило, розглядаєте все, що ви робите на екземплярі класу Test(перед тим, як використовувати його для інстанції екземплярів), як те, що знаходиться в області метапрограмування? Наприклад, ви змінюєте поведінку класу, роблячи Test.i = 0(тут ви просто знищуєте об'єкт властивості повністю). Я здогадуюсь, що "механізм властивості" запускається лише при доступі до властивостей в екземплярах класу (якщо, можливо, ви не змінюєте основну поведінку, використовуючи мета-клас як проміжний). Btw, будь ласка, закінчіть цю відповідь :-)
Оле Томсен Буус

1
@RickTeachey Спасибі :-) Ваш метаклас зрештою цікавий, але насправді трохи надто складний для мого вподобання. Це може бути корисно у великих рамках / застосуванні, коли цей механізм абсолютно необхідний. Так чи інакше, це свідчить про те, що якщо мета-поведінка, яка не використовується за замовчуванням, справді потрібна, Python це робить можливим :)
Оле Томсен Буус

1
@OleThomsenBuus: Перевір мою відповідь на простіший метаклас, який виконує цю роботу.
Етан Фурман

1
@taper Ви маєте рацію; Я відредагував відповідь, щоб усунути проблему (не можу повірити, що так довго сидить там неправильно!). Вибачте за непорозуміння.
Рік підтримує Моніку

33

Ви також можете додати змінні класу до класів на ходу

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

І екземпляри класу можуть змінювати змінні класу

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

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

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

3
Чи дотримуватимуться нові змінні класу, навіть якщо клас імпортується в інший модуль?
zakdances

Так. Класи фактично є однотонними, незалежно від простору імен, з яких ви їх викликаєте.
Педро

@Gregory ви сказали "І екземпляри класу можуть змінювати змінні класу". Насправді цей приклад називається доступом не модифікацією. Модифікацію здійснював сам об’єкт за допомогою власної функції append ().
Amr ALHOSSARY

19

Особисто я б використовував класний метод, коли мені потрібен був статичний метод. Головним чином тому, що я отримую клас як аргумент.

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

або використовувати декоратор

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

Для статичних властивостей .. Його час шукати певне визначення пітона .. змінна завжди може змінюватися. Існує два типи їх змінних та незмінних. Також є атрибути класу та атрибути екземплярів. Нічого, як статичні атрибути у значенні java & c ++

Навіщо використовувати статичний метод у пітонічному сенсі, якщо він не має жодного відношення до класу! Якби я був ти, я б або застосував classmethod або визначив метод, незалежний від класу.


1
Змінні не змінюються та не змінюються; об’єкти є. (Однак об’єкт може з різним ступенем успіху намагатися не допустити присвоєння певним його атрибутам.)
Девіс Хелінг

Java та C ++ використовують статичну (неправильне використання слова, imho) саме так, як ви використовуєте атрибут екземпляра проти класу. Атрибут / метод класу є статичним в Java та C ++, різниці немає, за винятком того, що в Python першим параметром для виклику методу класу є клас.
Angel O'Sphere

16

Особливу увагу, що стосується статичних властивостей та властивостей екземпляра, показано у прикладі нижче:

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

Це означає, що перед призначенням значення властивості екземпляра, якщо ми намагаємося отримати доступ до властивості через екземпляр, використовується статичне значення. Кожна властивість, задекларована в класі python, завжди має статичний слот у пам'яті .


16

Статичні методи в python називаються classmethod s. Погляньте на наступний код

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 , ми отримуємо помилку. Це тому, що вимагає виклику цього методу в екземплярі цього класу. Метод myStaticMethod встановлюється як класний метод за допомогою декоратора @classmethod .

Тільки для ударів і хихикань ми могли б викликати myInstanceMethod в класі, передавши екземпляр класу, як:

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

2
Гм ... статичні методи виготовляються за допомогою @staticmethod; @classmethod(очевидно) для методів класу (які в першу чергу призначені для використання в якості альтернативних конструкторів, але можуть служити в дрібнику як статичні методи, які трапляються, щоб отримати посилання на клас, через який вони були викликані).
ShadowRanger

11

Коли визначають деяку змінну-члена поза будь-яким методом-члена, змінна може бути статичною або нестатичною залежно від способу вираження змінної.

  • CLASSNAME.var - статична змінна
  • INSTANCENAME.var не є статичною змінною.
  • self.var всередині класу не є статичною змінною.
  • var всередині функції члена класу не визначено.

Наприклад:

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

Результати є

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

Відступ порушено. Це не виконає
Томас Веллер

9

Можна мати staticзмінні класу, але, мабуть, не варто докладати зусиль.

Ось доказ концепції написаний на Python 3 - якщо будь-яка з точних деталей помилкова, код можна налаштувати так, щоб він відповідав майже тому, що ви маєте на увазі 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)

і використовується:

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

і деякі тести:

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

Ви також можете примусити клас бути статичним за допомогою метакласу.

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

Тоді, коли ви випадково намагаєтеся ініціалізувати MyClass, ви отримаєте StaticClassError.


4
Чому це навіть клас, якщо ви не збираєтесь його інстанціювати? Це схоже на скручування Python, щоб перетворити його на Java ....
Нед Батчелдер,

1
Borg ідіома це кращий спосіб впоратися з цим.
Рік підтримує Моніку

@NedBatchelder Це абстрактний клас, призначений лише для підкласингу (та інстанціювання підкласів)
stevepastelan

1
Я сподіваюся, що підкласи не використовують super () для виклику __new__батьків ...
Ned Batchelder

7

Один дуже цікавий момент пошуку атрибутів Python - це те, що його можна використовувати для створення " віртуальних змінних":

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!

Зазвичай після їх створення немає жодних призначень. Зауважте, що пошук використовується selfтому, що, хоча він labelє статичним, тобто не асоціюється з певним екземпляром, значення все ще залежить від (класу) екземпляра.


6

Що стосується цієї відповіді , для постійної статичної змінної можна використовувати дескриптор. Ось приклад:

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

в результаті чого ...

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

Ви завжди можете створити виняток, якщо тихо ігнорування встановленого значення ( passвище) не є вашою справою. Якщо ви шукаєте C ++, статичну змінну класу стилю 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

Перегляньте цю відповідь та офіційні документи HOWTO для отримання додаткової інформації про дескриптори.


2
Ви також можете просто використовувати @property, що те саме, що використовувати дескриптор, але це набагато менше коду.
Рік підтримує Моніку

6

Абсолютно Так, Python сам по собі не має явного статичного члена даних, але ми можемо зробити це

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

вихід

0
0
1
1

пояснення

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

6

Так, напевно можна записати статичні змінні та методи в python.

Статичні змінні: Змінна, оголошена на рівні класу, називається статичною змінною, до якої можна отримати доступ безпосередньо за допомогою імені класу.

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

    >>> print(A.my_var)
        shagun

Змінні екземпляри: Змінні, які пов'язані між собою та доступ до них за допомогою екземпляра класу, - це змінні екземпляри.

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

Статичні методи: Подібно до змінних, до статичних методів можна отримати доступ безпосередньо з використанням класу Name. Не потрібно створювати екземпляр.

Але майте на увазі, статичний метод не може викликати нестатичний метод у python.

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

4

Щоб уникнути будь-якої потенційної плутанини, я хотів би протиставити статичні змінні та незмінні об’єкти.

Деякі примітивні типи об'єктів, такі як цілі числа, поплавці, рядки та тюлі, незмінні в Python. Це означає, що об'єкт, на який посилається дане ім'я, не може змінюватися, якщо він є одним із вищезазначених типів об'єкта. Ім’я можна перепризначити іншому об'єкту, але сам об'єкт може не змінюватися.

Здійснення статичної змінної робить цей крок далі, забороняючи ім'я змінної вказувати на будь-який об'єкт, окрім того, на який він в даний час вказує. (Примітка. Це загальна концепція програмного забезпечення, яка не є специфічною для Python; будь ласка, дивіться публікації інших щодо інформації про реалізацію статики в Python).


4

Найкращий спосіб, який я знайшов, - це використовувати інший клас. Ви можете створити об’єкт, а потім використовувати його на інших об'єктах.

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

На прикладі, наведеному вище, я склав клас з назвою staticFlag.

Цей клас повинен представляти статичну змінну __success(Private Static Var).

tryIt клас представляв звичайний клас, який нам потрібно використовувати.

Тепер я зробив об’єкт для одного прапора ( staticFlag). Цей прапор буде надісланий як посилання на всі звичайні об'єкти.

Усі ці об’єкти додаються до списку tryArr.


Результати цього сценарію:

False
False
False
False
False
True
True
True
True
True

2

Статичні змінні в класовому заводському пітоні3.6

Для всіх, хто використовує фабрику класів з python3.6 і вище, використовуйте nonlocalключове слово, щоб додати його до області / контексту класу, який створюється так:

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

так, але в цьому випадку hasattr(SomeClass, 'x')є False. я сумніваюся, це те, що хтось має на увазі під статичною змінною взагалі.
Рік підтримує Моніку

@RickTeachey хаха, побачив ваш статичний код змінної, stackoverflow.com/a/27568860/2026508 +1 Інтернет-сер, і я думав, що hasattr не працює так? так це some_varнезмінне і статично визначене, чи ні? Що має зовнішній доступ до отримання доступу до змінної, статичної чи ні? У мене зараз так багато питань. хотілося б почути деякі відповіді, коли знайдете час.
jmunsch

Так, цей метаклас досить смішний. Я не впевнений, що розумію питання, але, на мою думку, some_varвище це зовсім не член класу. У Python до всіх членів класу можна звернутися поза класом.
Рік підтримує Моніку

nonlocalKeywoard «врізається» обсяг змінної. Область визначення тіла класу не залежить від сфери, в якій він опиняється, коли ви говорите nonlocal some_var, що це просто створення не локальної (читайте: НЕ в області визначення класу) імені посилання на інший названий об'єкт. Тому він не приєднується до визначення класу, оскільки він не входить в область тіла класу.
Рік підтримує Моніку

1

Отже, це, мабуть, хак, але я використовував eval(str)для отримання статичного об'єкта, свого роду протиріччя, у python 3.

Існує файл Records.py, в якому немає нічого, крім classоб'єктів, визначених статичними методами та конструкторами, які зберігають деякі аргументи. Потім з іншого .py-файлу, import Recordsале мені потрібно динамічно вибирати кожен об'єкт, а потім інстанціювати його на вимогу відповідно до типу даних, що читаються.

Отже, куди object_name = 'RecordOne'або ім'я класу, я закликаю, cur_type = eval(object_name)а потім інстанціювати це ви робите. cur_inst = cur_type(args) Однак перед тим, як інстанціювати, ви можете зателефонувати статичним методам, cur_type.getName()наприклад, на зразок абстрактної реалізації базового класу або будь-якої мети. Однак у бекенді він, ймовірно, інстанціюється в python і не є справді статичним, тому що eval повертає об'єкт .... який повинен бути інстанційним .... що дає статичну поведінку.


0

Ви можете використовувати список або словник, щоб отримати "статичну поведінку" між екземплярами.

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

Якщо ви намагаєтеся поділитись статичною змінною, наприклад, збільшивши її в інших примірниках, щось подібне до цього сценарію працює добре:

# -*- 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.