Получить значение статической переменной

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

class GetPartition(Partition):
    _i = 100
    def __init__(self):
        super(Partition,self).__init__("get")
    def get_i(self):
        return type(self)._i
    def set_i(self,val):
        type(self)._i = val
    i = property(get_i, set_i)

и при необходимости это класс Partition:

class Partition(BaseCommand):
    def __init__(self,type):
        super(Partition,self).__init__("databaseTest")
        self.type = type

Итак, присваивая значение i из другого класса, я назначаю его прямо так:

GetPartition.i = 5

и среди этого класса при печати GetPartition.i он дает мне 5, но при попытке получить это значение из другого класса:

partitionNum = GetPartition()
print(partitionNum.i) # 100
print(partitionNum.get_i()) # 100
print(GetPartition.i) # <property object at 0x12A938D0>
print(GetPartition._i) # 100

Ответ 1

Как я объяснил в своем комментарии, проблема возникает, когда вы назначаете от 5 до i посредством:

GetPartition.i = 5

С помощью этой строки кода вы перезаписываете свойство и "обходите" свойство setter. Я имею в виду: setter не называется, когда вы вызываете его имя атрибута из класса; он вызывается только при вызове его имени атрибута из экземпляра класса.

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

Вы можете подтвердить эту проблему, выполнив следующие действия:

gp = GetPartition()
print(GetPartition.i) # the property is returned
GetPartition.i = 5 # here the property is overwritten
print(GetPartition.i) # 5 ; the property is gone
print(gp.i) # 5 because gp instance doesn't have its own i
gp.i = 2 # now gp does have its own i
print(gp.i) # 2
print(GetPartition.i) # 5 ; i is not synced

Как я сказал выше, геттеры и сеттеры property (и дескрипторы в целом) работают только с экземплярами GetPartition, а не с самим классом. Их можно заставить работать с самим классом, создав метакласс - класс класса - для вашего класса; это считается "глубокой черной магией" многих людей, и я не рекомендую идти этим путем, если вы можете избежать этого.

Я считаю, что приведенный ниже пример, вероятно, самый простой способ реализовать поведение, которое вы хотите. Этот подход отказывается от использования свойств в пользу переопределения методов getter и setter напрямую:

class Example():
    i = 1 # this is a "static variable"
    j = 3 # this is a regular class attribute
    #designate which of the class attributes are "static"
    statics = {'i'}
    def __getattribute__(self, attr):
        '''Overrides default attribute retrieval behavior.'''
        if attr in Example.statics:
            #use class version if attr is a static var
            return getattr(Example, attr)
        else:
            #default behavior if attr is not static var
            return super().__getattribute__(attr)
    def __setattr__(self, attr, value):
        '''Overrides default attribute setting behavior.'''
        if attr in Example.statics:
            #use class version if attr is a static var
            setattr(Example, attr, value)
        else:
            #default behavior if attr is not static var
            super().__setattr__(attr, value)

#testing
if __name__ == '__main__':
    print("\n\nBEGIN TESTING\n\n")

    e = Example()
    #confirm instance and class versions of i are the same
    test = "assert e.i is Example.i"
    exec(test)
    print(test)

    e.i = 5
    #confirm they remain the same after instance change
    test = "assert e.i is Example.i"
    exec(test)
    print(test)

    Example.i = 100
    #confirm they remain the same after class change
    test = "assert e.i is Example.i"
    exec(test)
    print(test)

    e.j = 12
    #confirm both versions of j are distinct
    test = "assert e.j is not Example.j"
    exec(test)
    print(test)

    print("\n\nTESTING COMPLETE\n\n")

Если вы не знакомы с __getattribute__ и __setattr__, я должен сообщить вам, что переопределение их часто довольно опасно и может вызвать большие проблемы (особенно __getattribute__). Вы обнаружите, что многие просто говорят: "Не делайте этого, переосмыслите свою проблему и найдите другое решение". Правильное выполнение переопределений требует глубокого понимания широкого круга вопросов python.

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