Возможно ли иметь статические переменные или методы класса в Python? Какой синтаксис требуется для этого?
использование термина статический для чего-то, что существует для всех экземпляров класса, всегда кажется мне странным
@ TonySuffolk66 Виновен (я думаю) C++, который просто присвоил существующее ключевое слово «static» из C (где он указывал, что время жизни переменной сохраняется за пределами области, в которой она была объявлена). C++ расширил это понятие, чтобы обозначить переменную, значение которой выходит за пределы «области действия» одного экземпляра класса. Python (более логично) просто называет их атрибутами класса, поскольку они являются атрибутами, связанными с самим классом, а не экземпляром класса.






Переменные, объявленные внутри определения класса, но не внутри метода, являются классовыми или статическими переменными:
>>> 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 рекомендует classmethods вместо staticmethod, поскольку метод затем получает тип класса в качестве первого аргумента, но я все еще немного не уверен в преимуществах этого подхода перед staticmethod. Если вы тоже, то это, вероятно, не имеет значения.
Я только изучаю Python, но преимущества @classmethod перед @staticmethod AFAIK в том, что вы всегда получаете имя класса, для которого был вызван метод, даже если это подкласс. Статическому методу не хватает этой информации, поэтому он не может, например, вызвать переопределенный метод.
@Blair Я хочу этим заняться. Например, если вы хотите иметь возможность многократно обращаться к PI = 3.14 в классе (игнорируя математические библиотеки ради аргумента), действительно ли вы используете MyClass.PI во всех этих местах? Что такое питонический подход? Разве это не выглядит загроможденным и некрасивым? Или я не прав?
@theJollyS в питоническом смысле для констант - не выращивать класс для констант. Просто возьмите const.py с PI = 3.14, и вы сможете импортировать его куда угодно. from const import PI
Этот ответ может запутать проблему статической переменной. Начнем с того, что i = 3 - это статическая переменная нет, это атрибут класса, и, поскольку он отличается от атрибута i на уровне экземпляра, нет ведет себя как статическая переменная в других языках. См. ответ Миллердева, Ответ Янна и мой ответ ниже.
Кажется, такая переменная класса недоступна во время __del__? По моему ограниченному опыту, переменные класса исчезают до запуска __del__. Ожидается ли это?
Превосходство @classmethod над @staticmethod заключается в том, что IMO легко реорганизовать методы класса и разбить их на более мелкие, тогда как с @staticmethod вы в основном застряли на том, что у вас есть. Рефакторинг статического метода C.m() потребует добавления ссылки на класс C к каждому вызову, не говоря уже о выборке полей класса. В некоторых случаях это, вероятно, имеет негативные побочные эффекты на полиморфизм.
Это привело бы к совершенно другому поведению, если бы вы использовали изменяемую переменную, такую как список: i = [], тогда m.i.append (1) также изменит содержимое переменной в классе.
так что только одна копия i (статическая переменная) будет в памяти, даже если я создам сотни экземпляров этого класса?
Для всех, кто интересуется, кого Дэниел упомянул в комментарии @Dubslow, это Millerdev (обратная машина)
@sdream да, поскольку единственная ссылка на i - это его присвоение классу. Чтобы получить доступ к этому из экземпляра через instance.__class__.i
Я смутно помню, как читал некоторые комментарии Гвидо ван Россума относительно введения классов и статических методов. IIRC, статические методы - это то, что больше соответствует методам классов на других языках. Python classmethod был более или менее «ошибкой», хотя он стал естественным способом определения альтернативного конструктора класса для использования вместо __new__.
Это немного пугает, если исходить из других языков программирования и ожидать, что это будет просто частное / защищенное поле.
Лично я бы использовал метод класса всякий раз, когда мне нужен статический метод. В основном потому, что я получаю класс в качестве аргумента.
class myObj(object):
def myMethod(cls)
...
myMethod = classmethod(myMethod)
или используйте декоратор
class myObj(object):
@classmethod
def myMethod(cls)
Для статических свойств ... Пора вам поискать определение Python ... переменная всегда может измениться. Есть два их типа: изменяемые и неизменяемые. Также есть атрибуты класса и атрибуты экземпляра. Ничего подобного статическим атрибутам в смысле java и C++.
Зачем использовать статический метод в питоническом смысле, если он не имеет никакого отношения к классу! На вашем месте я бы либо использовал метод класса, либо определил метод независимо от класса.
Переменные не являются изменяемыми или неизменяемыми; объекты есть. (Однако объект может с разной степенью успеха попытаться предотвратить присвоение определенных его атрибутов.)
Java и C++ используют статический (неправильное использование слова, imho) точно так же, как вы используете экземпляр по сравнению с атрибутом класса. Атрибут / метод класса статичен в Java и C++, никакой разницы, за исключением того, что в Python первым параметром при вызове метода класса является класс.
Статические методы в Python называются classmethods. Взгляните на следующий код
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
Эмм ... статические методы сделаны с @staticmethod; @classmethod (очевидно) предназначен для методов класса (которые в первую очередь предназначены для использования в качестве альтернативных конструкторов, но в крайнем случае могут служить статическими методами, которые получают ссылку на класс, через который они были вызваны).
@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
Я предлагаю вам немного расширить пример: если после установки Test.i = 6 вы затем создаете экземпляр нового объекта (например, u = Test ()), новый объект «унаследует» новое значение класса (например, ui == 6)
Один из способов синхронизировать статические переменные - сделать их свойствами: 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.
Итак, я мог бы сказать, что все переменные изначально статические, а затем доступ к экземплярам делает переменные экземпляра во время выполнения?
Возможно, это интересно: если вы определите метод в Test, который изменяет Test.i, это повлияет на ОБЕИХ значения Test.i и t.i.
@millerdev, как и вы упомянули, Python не имеет статических переменных, как у C++ или JAVA ... Так что можно будет сказать, что Test.i - это скорее переменная класса, чем статическая переменная?
Чтобы избежать возможной путаницы, я хотел бы противопоставить статические переменные и неизменяемые объекты.
Некоторые примитивные типы объектов, такие как целые числа, числа с плавающей запятой, строки и пары, неизменяемы в Python. Это означает, что объект, на который ссылается данное имя, не может измениться, если он относится к одному из вышеупомянутых типов объектов. Имя можно переназначить другому объекту, но сам объект не может быть изменен.
Сделав переменную статической, мы сделаем еще один шаг вперед, запретив имя переменной указывать на любой объект, кроме того, на который оно указывает в данный момент. (Примечание: это общая концепция программного обеспечения, не относящаяся к Python; информацию о реализации статики в Python см. В других сообщениях).
Вы также можете добавлять переменные класса в классы на лету
>>> 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]
Сохранятся ли новые переменные класса, даже если класс будет импортирован в другой модуль?
Да. Классы фактически являются одиночными, независимо от того, из какого пространства имен вы их вызываете.
@Gregory, вы сказали: «И экземпляры класса могут изменять переменные класса» На самом деле этот пример называется доступом, а не модификацией. Модификация была произведена самим объектом с помощью его собственной функции append ().
Вы также можете заставить класс быть статическим, используя метакласс.
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
Тогда всякий раз, когда вы случайно попытаетесь инициализировать Мой класс, вы получите StaticClassError.
Почему это вообще класс, если вы не собираетесь его создавать? Это похоже на поворот Python, чтобы превратить его в Java ...
Идиома борга - лучший способ справиться с этим.
@NedBatchelder Это абстрактный класс, предназначенный только для создания подклассов (и создания экземпляров подклассов)
Я надеюсь, что подклассы не используют super () для вызова __new__ своих родителей ...
Особо следует отметить статические свойства и свойства экземпляра, показанные в примере ниже:
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, всегда имеет статический слот в памяти..
Лучший способ, который я нашел, - использовать другой класс. Вы можете создать объект, а затем использовать его на других объектах.
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 (частная статическая переменная).
Класс tryIt представляет собой обычный класс, который нам нужно использовать.
Сейчас сделал объект для одного флага (staticFlag). Этот флаг будет отправлен как ссылка на все обычные объекты.
Все эти объекты добавляются в список tryArr.
Результаты этого сценария:
False
False
False
False
False
True
True
True
True
True
При определении некоторой переменной-члена вне любого метода-члена переменная может быть статической или нестатической в зависимости от того, как выражается переменная.
Например:
#!/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
Отступ нарушен. Это не будет выполнено
Что касается этого отвечать, для статической переменной постоянный вы можете использовать дескриптор. Вот пример:
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
Взгляните на этот ответ и официальную документацию КАК для получения дополнительной информации о дескрипторах.
Вы также можете просто использовать @property, что аналогично использованию дескриптора, но это намного меньше кода.
Как отмечали другие ответы, статические методы и методы класса легко выполняются с использованием встроенных декораторов:
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() ни один из аргументов не связан, и иметь аргументы вообще необязательно.
Однако реализация «статических переменных» (ну, статических переменных изменчивый, во всяком случае, если это не противоречие терминологии ...) не так проста. Как и millerdev указал в своем ответе, проблема в том, что атрибуты класса 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
Обратите внимание, что приведенные выше методы работают только с экземпляры вашего класса - они будут нет работать с при использовании самого класса. Так например:
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», класс вернется к поведению при получении / установке / удалении атрибута по умолчанию (то есть «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-val ue
@RickTeachey: Я думаю, вам следует обычно рассматривать все, что вы делаете в классе Instance Test (перед его использованием для создания экземпляров), как относящееся к области метапрограммирования? Например, вы изменяете поведение класса, выполняя Test.i = 0 (здесь вы просто полностью уничтожаете объект свойства). Я предполагаю, что «механизм свойств» срабатывает только при доступе к свойствам экземпляров класса (если, возможно, вы не измените базовое поведение, используя мета-класс в качестве промежуточного звена). Кстати, закончите, пожалуйста, этот ответ :-)
@OleThomsenBuus Эм, я бы не сказал так. Правильнее было бы назвать это «программированием объекта класса» и «программированием объекта экземпляра», потому что классы ЯВЛЯЮТСЯ объектами. Мета-программирование необходимо (в данном конкретном случае) только в том случае, если вы хотите стереть грань между экземпляром и классом, где находится «статическая переменная». Поведение property работает, потому что объекты экземпляра делегируют доступ к атрибутам своему классу в определенных случаях (например, когда атрибут не существует). Также: я обещаю, что дойду до того, что задумал для этого ответа. Просто был занят.
@RickTeachey Спасибо :-) В конце концов, ваш метакласс интересен, но, на мой взгляд, слишком сложен. Это может быть полезно в большом фреймворке / приложении, где этот механизм абсолютно необходим. Во всяком случае, это показывает, что если действительно необходимо новое (сложное) нестандартное мета-поведение, Python делает это возможным :)
@OleThomsenBuus: проверьте мой ответ на наличие более простого метакласса, который выполняет эту работу.
Я попробовал и обнаружил, что @property не синхронизируется между экземплярами классов в python 3. Это особенность старого языка Python?
@taper, как объяснено в ответе выше, @property синхронизируется между экземплярами класса (для всех версий python, возвращающихся на долгое время). член класса с тем же именем, что и свойство, прикрепленное к самому объекту класса, является нет синхронно с экземплярами.
Уважаемый @RickTeachey, я не могу понять вашу точку зрения из-за некоторых терминов, с которыми я не знаком. Но то, что я имею в виду, может быть продемонстрировано этим суть. Если вы запустите его в python 3.6.3 (что я и сделал), он даст другой результат, заявленный в этом ответе.
@taper Вы правы; Я отредактировал ответ, чтобы исправить проблему (не могу поверить, что он так долго сидел там неправильно!). Извините за путаницу.
Очень нравится писать. Один придирчивый выбор: неизменная версия на самом деле не является неизменной, потому что вы можете назначить лямбду для get_i, которая возвращает другое значение.
@HowardLovatt, на самом деле это не так. Если вы попытаетесь установить патч get_i после того, как свойство было создано, оно не перезапишет свойство. попытайся.
Следующие изменения t.i для меня: t.get_i = lambda s: 7 ; assert t.i == 7 # ERROR i changed.
@HowardLovatt, ваш оператор assert должен быть assert t.i == 3, и ошибки нет.
@RickTeachey Нет, для меня он проходит assert, т.е. t.i равен 7 - он изменился. Это верно для Pythonista, который использует 3.6 (я думаю) и 3.7 (через PyCharm).
@HowardLovatt, это странно. Это определенно не изменит значение ни в одной из версий python, которые я когда-либо использовал, если вы сначала установите свойство, а затем замените геттер (или сеттер) без перезаписи свойства. Это просто не так.
Можно иметь переменные класса 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
Для всех, кто использует фабрику классов с python3.6 и выше, используйте ключевое слово nonlocal, чтобы добавить его в область / контекст создаваемого класса следующим образом:
>>> def SomeFactory(some_var=None):
... class SomeClass(object):
... nonlocal some_var
... def print():
... print(some_var)
... return SomeClass
...
>>> SomeFactory(some_var = "hello world").print()
hello world
да, но в данном случае hasattr(SomeClass, 'x') - это False. Я сомневаюсь, что это вообще то, что кто-то имеет в виду под статической переменной.
@RickTeachey lol, видел ваш код статической переменной, stackoverflow.com/a/27568860/2026508 +1 интернет, сэр, и я подумал, что hasattr так не работает? Итак, some_var неизменен и статически определен, или нет? Какое отношение имеет внешний доступ к геттеру к статической переменной или нет? у меня сейчас так много вопросов. хотелось бы услышать ответы, когда у вас будет время.
Да, этот метакласс довольно нелепый. Я не уверен, что понимаю вопросы, но, на мой взгляд, some_var, приведенный выше, вообще не является членом класса. В Python ко всем членам класса можно получить доступ извне.
Клавиатура nonlocal «увеличивает» область действия переменной. Область определения тела класса не зависит от области, в которой он находится - когда вы говорите nonlocal some_var, это просто создает нелокальную (читай: НЕ в области определения класса) ссылку на имя другого именованного объекта. Поэтому он не привязывается к определению класса, потому что он не входит в область видимости тела класса.
Абсолютно да, Сам по себе 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"
Один очень интересный момент в поиске атрибутов 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
Статические методы: Подобно переменным, к статическим методам можно обращаться напрямую, используя имя класса. Нет необходимости создавать экземпляр.
Но имейте в виду, что статический метод не может вызывать нестатический метод в python.
>>> class A:
... @staticmethod
... def my_static_method():
... print("Yippey!!")
...
>>> A.my_static_method()
Yippey!!
Я думаю, что то, что вы называете «статическими» переменными, - это переменные класса. Визуализация: class A (): inner_var = 0 class B (A): pass A.inner_var = 15 B.inner_var = 30 print ("A: static =" + str (A.inner_var)) print ("B: static = "+ str (B.inner_var)) # Вывод: # A: static = 15 # B: static = 30
Вы можете использовать список или словарь, чтобы получить «статическое поведение» между экземплярами.
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
Так что это, вероятно, взлом, но я использовал 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 возвращает объект ... который должен быть создан ... который дает статическое поведение.
Если вы пытаетесь поделиться статической переменной, например, для ее увеличения в других экземплярах, что-то вроде этого скрипта отлично работает:
# -*- 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)
Скажем так, статическая переменная создается, когда создается пользовательский класс, и определяется статическая переменная, которая должна следовать за ключевым словом self,
class Student:
the correct way of static declaration
i = 10
incorrect
self.i = 10
Определения @dataclass предоставляют имена уровня класса, которые используются для определения переменных экземпляра и метода инициализации __init__(). Если вам нужна переменная уровня класса в @dataclass, вы должны использовать подсказку типа typing.ClassVar. Параметры типа ClassVar определяют тип переменной уровня класса.
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)
В отличие от @staticmethod, переменные класса являются статическим методом класса и используются всеми экземплярами.
Теперь вы можете получить к нему доступ, как
instance = MyClass()
print(instance.i)
или же
print(MyClass.i)
вы должны присвоить значение этим переменным
я пытался
class MyClass:
i: str
и присвоение значения в одном вызове метода, в этом случае он не будет работать и выдаст ошибку
i is not attribute of MyClass
Да. Отсутствие ключевого слова static может вводить в заблуждение, но любой объект, инициализированный внутри класса (только один отступ внутри класса, а не в конструкторе), является статическим. Он не зависит от создания экземпляра (потому что он не является частью конструктора). Что касается методов, вы можете сделать это с помощью декоратора @staticmethod.