Чи можливі змінні статичного класу в 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.