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


Возможно ли иметь статические переменные класса или методы в python? Какой синтаксис требуется для этого?


1481


источник


Ответы:


Переменные, объявленные внутри определения класса, но не внутри метода, являются переменными класса или статики:

>>> 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 s по методу staticmethod, поскольку метод затем получает тип класса в качестве первого аргумента, но я все еще немного нечеткий в преимуществах этого подхода по сравнению с staticmethod. Если вы тоже, то это, вероятно, не имеет значения.


1453



@Blair Конрад сказал, что статические переменные, объявленные внутри определения класса, но не внутри метода являются классными или «статическими» переменными:

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

Здесь есть несколько gotcha. Выполняя приведенный выше пример:

>>> 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. Первый аргумент 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 the the same as  Test.i = arg1

Pictorial Representation Of Above Example


518



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

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

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 в свойство:

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

Я представляю решение вышеприведенного «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». Это делается путем поиска запрашиваемого атрибута в наборе «статических переменных» для данного класса (или его родительских классов). Если атрибут не отображается в «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

137



Вы также можете добавлять переменные класса в классы на лету

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

23



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

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

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

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

Для статических свойств. Его время, когда вы смотрите какое-то определение python .. переменная всегда может измениться. Существует два типа изменяемых и неизменяемых типов. Также существуют атрибуты класса и атрибуты экземпляра. Ничто не похоже на статические атрибуты в смысле java и c ++

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


12



Статические методы в 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

11



One special thing to note about static properties & instance properties, shown in the example below:

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

This means before assigning the value to instance property, if we try to access the property thru' instance, the static value is used. Each property declared in python class always has a static slot in memory.


10



When define some member variable outside any member method, the variable can be either static or non-static depending on how the variable is expressed.

  • CLASSNAME.var is static variable
  • INSTANCENAME.var is not static variable.
  • self.var inside class is not static variable.
  • var inside the class member function is not defined.

For example:

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

The results are

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

7



You could also enforce a class to be static using metaclass.

class StaticClassError(Exception):
    pass


class StaticClass:
    __metaclass__ = abc.ABCMeta

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

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

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

Then whenever by accident you try to initialize MyClass you'll get an StaticClassError.


6



It is possible to have static class variables, but probably not worth the effort.

Here's a proof-of-concept written in Python 3 -- if any of the exact details are wrong the code can be tweaked to match just about whatever you mean by a 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)

and in use:

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

and some tests:

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

4



To avoid any potential confusion, I would like to contrast static variables and immutable objects.

Some primitive object types like integers, floats, strings, and touples are immutable in Python. This means that the object that is referred to by a given name cannot change if it is of one of the aforementioned object types. The name can be reassigned to a different object, but the object itself may not be changed.

Making a variable static takes this a step further by disallowing the variable name to point to any object but that to which it currently points. (Note: this is a general software concept and not specific to Python; please see others' posts for information about implementing statics in Python).


3



The best way I found is to use another class. You can create an object and then use it on other objects.

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

With the example above, I made a class named staticFlag.

This class should present the static var __success (Private Static Var).

tryIt class represented the regular class we need to use.

Now I made an object for one flag (staticFlag). This flag will be sent as reference to all the regular objects.

All these objects are being added to the list tryArr.


This Script Results:

False
False
False
False
False
True
True
True
True
True

3