Возможны ли статические переменные класса в Python?

1949

Возможно ли иметь статические переменные класса или методы в 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 .

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

@beidy рекомендует использовать classmethod вместо staticmethod, так как метод затем получает тип класса в качестве первого аргумента, но я все еще не совсем понимаю преимущества этого подхода по сравнению со staticmethod. Если вы тоже, то это, вероятно, не имеет значения.

Блэр Конрад
источник
11
Я только изучаю Python, но преимущество @classmethodнад @staticmethodAFAIK заключается в том, что вы всегда получаете имя класса, к которому был вызван метод, даже если это подкласс. Статическому методу не хватает этой информации, поэтому он не может вызвать переопределенный метод, например.
Себ
49
@theJollySin Питонический способ для констант - не создавать класс для констант. Просто некоторые const.pyс , PI = 3.14и вы можете импортировать его везде. from const import PI
Giszmo
31
Этот ответ может запутать проблему статических переменных. Начнем с того, что i = 3это не статическая переменная, это атрибут класса, и, поскольку он отличается от атрибута уровня экземпляра, iон не ведет себя как статическая переменная в других языках. См ответ millerdev в , ответ Yann, и мой ответ ниже.
Рик поддерживает Монику
2
таким образом, только одна копия i(статическая переменная) будет в памяти, даже если я создам сотни экземпляров этого класса?
sdream
2
Для всех, кто интересуется тем, кого Дэниел упомянул в комментарии @Dubslow, это millerdev ( машина обратного хода )
HeyJude
619

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

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

@beid также упомянул classmethod, который похож на staticmethod. Первым аргументом метода класса является объект класса. Пример:

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

Наглядное представление вышеприведенного примера

millerdev
источник
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, который изменяет Test.i, это повлияет на ОБА Test.i и значения ti.
Пабло
@millerdev, как вы упомянули, у Python нет статических переменных, как у C ++ или JAVA. Так что будет нормально сказать, что Test.i является скорее переменной класса, чем статической переменной?
Тайто
198

Статические и классовые методы

Как отмечалось в других ответах, статические методы и методы класса легко реализуются с помощью встроенных декораторов:

class Test(object):

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

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

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

Как обычно, первый аргумент to 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атрибута.

Частичное ожидаемое поведение статической переменной, т. Е. Синхронизация атрибута между несколькими экземплярами (но не с самим классом; см. «Полученная информация» ниже), может быть достигнута путем превращения атрибута класса в свойство:

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

Один Gotcha, чтобы быть в курсе

Обратите внимание, что приведенные выше методы работают только с экземплярами вашего класса - они не будут работать при использовании самого класса . Так, например:

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

Я представляю решение вышеупомянутой «ошибки» ниже; Тем не менее, я бы настоятельно рекомендовал вам - не пытаться делать что-то вроде следующего, пока - как минимум - вы полностью не поймете, почему 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

Ниже я создал метакласс, который пытается эмулировать поведение «статических переменных» других языков. Он в основном работает, заменяя стандартные методы получения, установки и удаления версиями, которые проверяют, является ли запрашиваемый атрибут «статической переменной».

Каталог «статических переменных» хранится в StaticVarMeta.staticsатрибуте. Все запросы к атрибутам первоначально пытаются разрешить с использованием порядка разрешения замены. Я назвал это «статическим разрешением» или «SRO». Это делается путем поиска запрошенного атрибута в наборе «статических переменных» для данного класса (или его родительских классов). Если атрибут не отображается в «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
Мухаммед Рефаат
@RickTeachey: Полагаю, вам следует рассматривать все, что вы делаете в классе Instance Test(до того, как использовать его для создания экземпляров), как область метапрограммирования? Например, вы изменяете поведение класса, выполняя Test.i = 0(здесь вы просто полностью уничтожаете объект свойства). Я предполагаю, что «механизм свойств» включается только при доступе к свойствам экземпляров класса (если, возможно, вы не измените базовое поведение, используя мета-класс в качестве промежуточного). Кстати, пожалуйста, закончите этот ответ :-)
Оле Томсен Буус
1
@RickTeachey Спасибо :-) Ваш метакласс в конце интересен, но на самом деле он слишком сложен для моего вкуса. Это может быть полезно в большой среде / приложении, где этот механизм абсолютно необходим. В любом случае, это иллюстрирует, что если действительно необходимо новое (сложное) нестандартное мета-поведение, Python делает это возможным :)
Ole Thomsen Buus
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
Будут ли новые переменные класса придерживаться, даже если класс будет импортирован в другой модуль?
Закданс
Да. Классы по сути являются одиночными, независимо от пространства имен, из которого вы их называете.
Педро
@Григорий, вы сказали: «И экземпляры классов могут изменять переменные класса». На самом деле этот пример называется доступом, а не модификацией. Модификация была сделана самим объектом через его собственную функцию append ().
Amr ALHOSSARY
19

Лично я использовал бы метод класса всякий раз, когда мне нужен статический метод. Главным образом потому, что я получаю класс в качестве аргумента.

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

или использовать декоратор

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

Для статических свойств ... Пришло время посмотреть какое-то определение Python. Переменная всегда может быть изменена. Есть два типа: изменяемые и неизменяемые. Кроме того, есть атрибуты класса и атрибуты экземпляра. Ничто на самом деле не похоже на статические атрибуты в смысле java & c ++.

Зачем использовать статический метод в питоническом смысле, если он не имеет никакого отношения к классу! Если бы я был тобой, я бы либо использовал метод класса, либо определил бы метод, независимый от класса.

Мойки
источник
1
Переменные не являются изменяемыми или неизменными; объекты есть. (Однако объект может с различной степенью успеха пытаться предотвратить присвоение некоторых его атрибутов.)
Дэвис Херринг,
Java и C ++ используют статические (некорректное использование слова, imho) точно так же, как вы используете атрибуты экземпляра и класса. Атрибут / метод класса является статическим в Java и C ++, без разницы, за исключением того, что в Python первым параметром для вызова метода класса является класс.
Ангел О'Сфера
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, всегда имеет статический слот в памяти .

jondinham
источник
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
willurd
источник
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
user2209576
источник
Отступ нарушен. Это не будет выполнено
Томас Уэллер
10

Можно иметь 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__своих родителей ...
Нед Бэтчелдер,
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 для получения дополнительной информации о дескрипторах.

Yann
источник
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

Статические переменные в фабрике классов python3.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
jmunsch
источник
да, но в этом случае hasattr(SomeClass, 'x')есть False. я сомневаюсь, что это то, что кто-то подразумевает под статической переменной вообще.
Рик поддерживает Монику
@RickTeachey lol, видел код вашей статической переменной, 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)
Зимний отряд
источник