【问题标题】:Get static variable value获取静态变量值
【发布时间】:2015-06-02 12:04:03
【问题描述】:

我正在尝试创建一个静态变量以通过不同的类访问,为其分配值,并在需要时获取该值。我确实使用this way 来实现这一点,这导致我包含以下属性:

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

【问题讨论】:

  • 当您通过GetPartition.i = 5 将5 分配给i 时,您将覆盖属性,并绕过 属性设置器。那个时候这个属性已经不存在了。您可以通过GetPartition.i = 5、gp = GetPartition()、gp.i = 2、print(gp.i, GetPartition.i) 来确认这一点。属性 getter 和 setter 仅适用于 GetPartition 的实例,而不适用于类本身。如果您想更新“静态变量”(请记住 Python 实际上没有静态变量),您需要像示例代码中那样执行 GetPartition._i = 5。
  • @RickTeachey 起初感谢您的回复,然后我尝试将其设置为 GetPartition._i = 5 但在尝试获取它时遇到了类似的问题,有什么想法如何从另一个班级获取它?
  • 从其他地方获取它的简单方法是使用print(GetPartition._i) 而不是print(GetPartition.i)。我意识到这不是最优的。还有其他方法可以完成您想要的,但我所知道的所有方法都非常复杂。

标签: python static class-variables


【解决方案1】:

正如我在评论中解释的那样,当您通过以下方式将 5 分配给 i 时,问题就出现了:

GetPartition.i = 5

使用这行代码,您将覆盖属性,并“绕过”属性设置器。我的意思是:当你从类中调用属性名称​​时,属性设置器不被调用;仅当您从类实例中调用其属性名称时才会调用它。

由于它已被覆盖,因此该属性不再存在,并且所有对 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 getter 和 setter(以及一般的描述符)仅适用于 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% 确定我上面给出的覆盖不会给你带来其他问题。我相信它们是合理的,但请注意 python 的这些特殊角落可能非常棘手。

【讨论】:

  • 感谢这个伟大的插图,事实上我尝试过这种方式但无法管理它,它在尝试设置值e.i = 5 时在super().__setattr__(attr, value) 行给了我super() takes at least 1 argument (0 given)。给它“自我”是行不通的,因为它需要某种类型。
  • 另外值得一提的是,我不会从同一个类中获得价值,而是从另一个具有不同布局的类中获得价值
猜你喜欢
  • 1970-01-01
  • 2012-08-18
  • 2011-07-28
  • 2016-12-20
  • 2013-03-30
  • 1970-01-01
  • 2011-09-23
  • 1970-01-01
相关资源
最近更新 更多