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

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

28 ответов

Решение

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

>>> 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. Если вы тоже, то это, вероятно, не имеет значения.

@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 the the same as  Test.i = arg1

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

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

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

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 приписывать.

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

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

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

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

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

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

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

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

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

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

Особое замечание о статических свойствах и свойствах экземпляра показано в примере ниже:

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, всегда имеет статический слот в памяти.

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

Можно иметь 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

При определении некоторой переменной-члена вне какого-либо метода-члена переменная может быть статической или нестатической в ​​зависимости от того, как переменная выражена.

  • 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

Определения @dataclass предоставляют имена уровня класса, которые используются для определения переменных экземпляра и метода инициализации, __init__(). Если вам нужна переменная уровня класса в @dataclass ты должен использовать <tcode id="350836"></tcode>введите подсказку. В ClassVar Параметры type определяют тип переменной уровня класса.

      from typing import ClassVar
from dataclasses import dataclass

@dataclass
class Test:
    i: ClassVar[int] = 10
    x: int
    y: int
    
    def __repr__(self):
        return f"Test({self.x=}, {self.y=}, {Test.i=})"

Примеры использования:

      > test1 = Test(5, 6)
> test2 = Test(10, 11)

> test1
Test(self.x=5, self.y=6, Test.i=10)
> test2
Test(self.x=10, self.y=11, Test.i=10)

Вы также можете заставить класс быть статичным, используя метакласс.

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.

С типами данных Object это возможно. Но с примитивными типами, такими как int, bool и т.д. bahaviour отличается от других ООП.

Пример:

      class A:
    static = 1


class B(A):
    pass


print(f"int {A.static}")  # get 1 correctly
print(f"int {B.static}")  # get 1 correctly

A.static = 5
print(f"int {A.static}")  # get 5 correctly
print(f"int {B.static}")  # get 5 correctly

B.static = 6
print(f"int {A.static}")  # expected 6, but get 5 incorrectly
print(f"int {B.static}")  # get 6 correctly

A.static = 7
print(f"int {A.static}")  # get 7 correctly
print(f"int {B.static}")  # get unchanged 6

Решение на библиотеки основеrefdatatypes :

      from refdatatypes.refint import RefInt


class AAA:
    static = RefInt(1)


class BBB(AAA):
    pass


print(f"refint {AAA.static.value}")  # get 1 correctly
print(f"refint {BBB.static.value}")  # get 1 correctly

AAA.static.value = 5
print(f"refint {AAA.static.value}")  # get 5 correctly
print(f"refint {BBB.static.value}")  # get 5 correctly

BBB.static.value = 6
print(f"refint {AAA.static.value}")  # get 6 correctly
print(f"refint {BBB.static.value}")  # get 6 correctly

AAA.static.value = 7
print(f"refint {AAA.static.value}")  # get 7 correctly
print(f"refint {BBB.static.value}")  # get 7 correctly

Одним из очень интересных моментов в поиске атрибутов 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 является статическим в том смысле, что не связан с конкретным экземпляром, значение по-прежнему зависит от (класса) экземпляра.

Да, безусловно, можно писать статические переменные и методы в 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!!

Что касается этого ответа, для постоянной статической переменной вы можете использовать дескриптор. Вот пример:

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 для получения дополнительной информации о дескрипторах.

Абсолютно да, 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"

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

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

Обобщая и добавляя ответы других, существует множество способов объявить статические методы или переменные в python .

1. Использование в качестве декоратора:

Можно просто поместить декоратор над объявленным методом (функцией), чтобы сделать его статическим методом. Например,

      class Calculator:
    @staticmethod
    def multiply(n1, n2, *args):
        Res = 1
        for num in args: Res *= num
        return n1 * n2 * Res

print(Calculator.multiply(1, 2, 3, 4))              # 24

2. Использование staticmethod()staticmethod() в качестве функции параметра:

Этот метод может получать аргумент типа функции и возвращает статическую версию переданной функции. Например,

      class Calculator:
    def add(n1, n2, *args):
        return n1 + n2 + sum(args)

Calculator.add = staticmethod(Calculator.add)
print(Calculator.add(1, 2, 3, 4))                   # 10

3. Использование в качестве декоратора:

@classmethod оказывает такое же влияние на функцию, как и @staticmethod, но на этот раз в функции необходимо принять дополнительный аргумент (аналогично параметру self для переменных экземпляра). Например,

      class Calculator:
    num = 0
    def __init__(self, digits) -> None:
        Calculator.num = int(''.join(digits))

    @classmethod
    def get_digits(cls, num):
        digits = list(str(num))
        calc = cls(digits)
        return calc.num

print(Calculator.get_digits(314159))                # 314159

4. Использование classmethod()classmethod() в качестве функции-параметра:

@classmethod также можно использовать как функцию-параметр, если вы не хотите изменять определение класса. Например,

      class Calculator:
    def divide(cls, n1, n2, *args):
        Res = 1
        for num in args: Res *= num
        return n1 / n2 / Res

Calculator.divide = classmethod(Calculator.divide)

print(Calculator.divide(15, 3, 5))                  # 1.0

5. Прямое декларирование

Метод / переменная, объявленная вне всех других методов, но внутри класса автоматически статична.

      class Calculator:   
    def subtract(n1, n2, *args):
        return n1 - n2 - sum(args)

print(Calculator.subtract(10, 2, 3, 4))             # 1

Вся программа

      class Calculator:
    num = 0
    def __init__(self, digits) -> None:
        Calculator.num = int(''.join(digits))
    
    
    @staticmethod
    def multiply(n1, n2, *args):
        Res = 1
        for num in args: Res *= num
        return n1 * n2 * Res


    def add(n1, n2, *args):
        return n1 + n2 + sum(args)
    

    @classmethod
    def get_digits(cls, num):
        digits = list(str(num))
        calc = cls(digits)
        return calc.num


    def divide(cls, n1, n2, *args):
        Res = 1
        for num in args: Res *= num
        return n1 / n2 / Res


    def subtract(n1, n2, *args):
        return n1 - n2 - sum(args)
    



Calculator.add = staticmethod(Calculator.add)
Calculator.divide = classmethod(Calculator.divide)

print(Calculator.multiply(1, 2, 3, 4))              # 24
print(Calculator.add(1, 2, 3, 4))                   # 10
print(Calculator.get_digits(314159))                # 314159
print(Calculator.divide(15, 3, 5))                  # 1.0
print(Calculator.subtract(10, 2, 3, 4))             # 1

Обратитесь к документации Python для освоения ООП в python.

Чтобы избежать любой путаницы, я бы хотел сопоставить статические переменные и неизменяемые объекты.

Некоторые примитивные типы объектов, такие как целые числа, числа с плавающей запятой, строки и части, являются неизменяемыми в Python. Это означает, что объект, на который ссылается данное имя, не может измениться, если он относится к одному из вышеупомянутых типов объектов. Имя может быть переназначено другому объекту, но сам объект не может быть изменен.

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

Статические переменные в фабрике классов 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

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

# -*- 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)

Так что это, вероятно, взлом, но я использовал 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 возвращает объект... который должен быть создан... который дает статическое поведение.

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

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

Вы можете создать переменную класса x, переменная экземпляра name, метод экземпляра test1(self), метод класса test2(cls)и статический метод test3()как показано ниже:

      class Person:
    x = "Hello" # Class variable

    def __init__(self, name):
        self.name = name # Instance variable
    
    def test1(self): # Instance method
        print("Test1")

    @classmethod
    def test2(cls): # Class method
        print("Test2")
        
    @staticmethod
    def test3(): # Static method
        print("Test3")

Я объясняю переменную класса в своем ответе , методе класса и статическом методе в моем ответе и методе экземпляра в моем ответе .

Скажем так, статическая переменная создается при появлении определенного пользователем класса, а определение статической переменной должно следовать за ключевым словом self,

class Student:

    the correct way of static declaration
    i = 10

    incorrect
    self.i = 10

Не похоже на @staticmethod но переменные класса являются статическим методом класса и используются всеми экземплярами.

Теперь вы можете получить к нему доступ, как

      instance = MyClass()
print(instance.i)

или же

      print(MyClass.i)

вы должны присвоить значение этим переменным

я пытался

      class MyClass:
  i: str

и присвоение значения в одном вызове метода, в этом случае он не будет работать и выдаст ошибку

      i is not attribute of MyClass

Переменная класса и возможность создания подклассов

Предполагая, что вы ищете не действительно статическую переменную, а скорее что-то питоническое, которое будет выполнять ту же работу для согласных взрослых, затем используйте переменную класса. Это предоставит вам переменную, к которой все экземпляры могут получить доступ (и обновить)

Остерегайтесь: многие другие ответы, в которых используется переменная класса, нарушат подклассы. Вам следует избегать прямого обращения к классу по имени.

      from contextlib import contextmanager

class Sheldon(object):
    foo = 73

    def __init__(self, n):
        self.n = n

    def times(self):
        cls = self.__class__
        return cls.foo * self.n
        #self.foo * self.n would give the same result here but is less readable
        # it will also create a local variable which will make it easier to break your code
    
    def updatefoo(self):
        cls = self.__class__
        cls.foo *= self.n
        #self.foo *= self.n will not work here
        # assignment will try to create a instance variable foo

    @classmethod
    @contextmanager
    def reset_after_test(cls):
        originalfoo = cls.foo
        yield
        cls.foo = originalfoo
        #if you don't do this then running a full test suite will fail
        #updates to foo in one test will be kept for later tests

даст вам ту же функциональность, что и использование Sheldon.fooдля обращения к переменной и будет проходить такие тесты:

      def test_times():
    with Sheldon.reset_after_test():
        s = Sheldon(2)
        assert s.times() == 146

def test_update():
    with Sheldon.reset_after_test():
        s = Sheldon(2)
        s.updatefoo()
        assert Sheldon.foo == 146

def test_two_instances():
    with Sheldon.reset_after_test():
        s = Sheldon(2)
        s3 = Sheldon(3)
        assert s.times() == 146
        assert s3.times() == 219
        s3.updatefoo()
        assert s.times() == 438

Это также позволит кому-то еще просто:

      class Douglas(Sheldon):
    foo = 42

который также будет работать:

      def test_subclassing():
    with Sheldon.reset_after_test(), Douglas.reset_after_test():
        s = Sheldon(2)
        d = Douglas(2)
        assert d.times() == 84
        assert s.times() == 146
        d.updatefoo()
        assert d.times() == 168 #Douglas.Foo was updated
        assert s.times() == 146 #Seldon.Foo is still 73

def test_subclassing_reset():
    with Sheldon.reset_after_test(), Douglas.reset_after_test():
        s = Sheldon(2)
        d = Douglas(2)
        assert d.times() == 84 #Douglas.foo was reset after the last test
        assert s.times() == 146 #and so was Sheldon.foo

Чтобы получить отличные советы о том, на что следует обратить внимание при создании классов, посмотрите видео Рэймонда Хеттингера https://www.youtube.com/watch?v=HTLu2DFOdTg .

Просто создайте класс, который содержит переменные или типы объектов. Получите доступ к этим переменным, используя имя класса, как показано ниже:


    class StaticVariable:
        myvar1 = 1
        myvar2 = 2


    StaticVariable.myvar1 = StaticVariable.myvar1 +1
    StaticVariable.myvar2 = StaticVariable.myvar2 +1
Другие вопросы по тегам