Python类方法中的类C静态变量

时间:2019-06-28 18:46:02

标签: python class

经过20年的C ++经验,我正在努力学习Python。

现在,我想拥有一个方法(类中的函数),该方法具有自己的“静态”变量,而不是静态的 class 变量。

一个伪代码示例可以更好地说明我想要的内容。

class dummy:
    @staticmethod
    def foo():
        foo.counter += 1
        print "You have called me {} times.".format(foo.counter)
    foo.counter = 0

注意1:我只是为了简单起见使用@staticmethod,但这无关紧要。

注意2:这会与AttributeError: 'staticmethod' object has no attribute 'counter'崩溃,但是正如我上面所说的,这是一个伪代码,用于阐明我的目标。

我已经了解到,该方法在类之外 起作用:

def foo():
    foo.counter += 1
    print "You have called me {} times.".format(foo.counter)
foo.counter = 0

但是相同的技巧似乎不适用于成员函数。

最新信息,我只能使用Python 2.7(不是我的选择)。

是否有合法且可靠的方法来拥有一个范围限于成员函数范围的持久变量(或常量)?

一些相关链接

谢谢。

5 个答案:

答案 0 :(得分:2)

不,没有。您已经找到了Python版本:您是类dummy开发的最高霸主,只能在函数foo中访问的类变量。

如果可以帮助您了解其基本原理,则可以启动该路径here。我希望您已经经历了很多事情;但是,此答案为Python提供了更多细节,以实现您所需的更多Pythonic方法。

答案 1 :(得分:1)

正如@Prune已经提到的那样,没有真正的方法。

但是,如果希望方法中的静态变量仅可用于其所属的对象(就我所知,就象在C ++中一样),则应在构造函数中定义它或将其定义为类变量,一种非静态方法:

from __future__ import print_function

class dummy:
    def __init__(self, counter=0):
        self._foo_counter = 0

    def foo(self):
        self._foo_counter += 1
        print("You have called me {} times.".format(self._foo_counter))

或:

class dummy:
    def foo(self):
        self._foo_counter += 1
        print("You have called me {} times.".format(self._foo_counter))

    _foo_counter = 0

这样,运行:

x = dummy()
for _ in range(4):
    x.foo()

y = dummy()
for _ in range(4):
    y.foo()

结果:

You have called me 1 times.
You have called me 2 times.
You have called me 3 times.
You have called me 4 times.
You have called me 1 times.
You have called me 2 times.
You have called me 3 times.
You have called me 4 times.

请注意,这两个版本的行为方式并不完全相同。 当您直接在类中定义_foo_counter时,将可以同时访问对象(_foo_counter和类本身(self._foo_counter)的dummy._foo_counter变量。 dummy._foo_counter对于该类的每次使用都是静态的,并且将在该类的多个实例之间(因此在多个对象之间)保持不变。 如果在@staticmethod上使用dummy.foo()装饰器,这也是唯一可以访问的变量:

class dummy:
    @staticmethod
    def foo():
        dummy._foo_counter += 1
        print("You have called me {} times.".format(dummy._foo_counter))

    _foo_counter = 0

此处self_foo_counter将不可访问,并且您唯一的选择是使用整个类变量dummy._foo_counter(已经提及,您也可以使用未用@staticmethod装饰的方法。

以便再次运行:

x = dummy()
for _ in range(4):
    x.foo()

y = dummy()
for _ in range(4):
    y.foo()

导致:

You have called me 1 times.
You have called me 2 times.
You have called me 3 times.
You have called me 4 times.
You have called me 5 times.
You have called me 6 times.
You have called me 7 times.
You have called me 8 times.

答案 2 :(得分:1)

一种实现此目的的方法是将变量隐藏在一个闭包中,因此出于您的目的,它实际上将是静态的。不幸的是,Python 2不支持nonlocal关键字,因此我们必须将变量的值包装在一个对象中(除非您只是要表示 reference 而不是 mutate 方法中的变量(即分配给变量):

In [7]: class _Nonlocal:
   ...:     def __init__(self, value):
   ...:         self.counter = value
   ...:
   ...: def foo_maker():
   ...:     nonlocal = _Nonlocal(0)
   ...:     def foo(self):
   ...:         nonlocal.counter += 1
   ...:         print "You have called me {} times.".format(nonlocal.counter)
   ...:     return foo
   ...:

In [8]: class Dummy(object): #you should always inherit from object explicitely in python 2
   ...:     foo = foo_maker()
   ...:

In [9]: dummy = Dummy()

In [10]: dummy.foo()
You have called me 1 times.

In [11]: dummy.foo()
You have called me 2 times.

当然,只是为了避免使用实例变量,这是很多条件。也许 best 解决方案是使您的方法成为自定义对象,并且您可以实现描述符协议以使其可作为方法调用,并且如果您愿意,它将用作实例方法:

In [35]: import types
    ...:
    ...: class Foo(object):
    ...:     def __init__(this):
    ...:         this.counter = 0
    ...:     def __call__(this, self):
    ...:         this.counter += 1
    ...:         print "You have called me {} times.".format(this.counter)
    ...:         print "here is some instance state, self.bar: {}".format(self.bar)
    ...:     def __get__(this, obj, objtype=None):
    ...:         "Simulate func_descr_get() in Objects/funcobject.c"
    ...:         if obj is None:
    ...:             return this
    ...:         return types.MethodType(this, obj)
    ...:

In [36]: class Dummy(object): #you should always inherit from object explicitely in python 2
    ...:     foo = Foo()
    ...:     def __init__(self):
    ...:         self.bar = 42
    ...:

In [37]: dummy = Dummy()

In [38]: dummy.foo()
You have called me 1 times.
here is some instance state, self.bar: 42

In [39]: dummy.bar = 99

In [40]: dummy.foo()
You have called me 2 times.
here is some instance state, self.bar: 99

所有这些都是高度不规则的,并且会使习惯于python约定的其他人感到困惑,尽管我希望您能看到,Python数据模型提供了许多自定义内容的功能。

请注意,我使用this作为第一个参数的名称,以避免与self混淆,而混淆实际上来自Foo作为方法绑定到的对象。

我再次重申,我永远不会这样做。如果您的函数需要维护状态,则可以使用实例变量,或者使用生成器,并且可以将其用作迭代器。

答案 3 :(得分:1)

使用可变类型作为函数关键字参数的默认值可能是最简单的方法:

class Dummy:

    @staticmethod
    def foo(_counter=[0]):   # here using a list, but you could use a dictionary, or a deque
        _counter[0] += 1
        print "You have called me {} times.".format(_counter[0])

基本原理是此变量仅初始化一次;它的最新值保留在所形成的闭合中。

答案 4 :(得分:0)

我已经在旧帖子中发布了它,但没人注意到它

由于我有一个带有静态变量的惯用目标,因此我想介绍以下内容: 在一个函数中,我只想使用计算值初始化一次变量,这可能会有点昂贵。 因为我喜欢出色的写作,并且是一名老的C风格程序员。我试图定义类似宏的文字:

def  Foo () :
   StaticVar( Foo, ‘Var’, CalculateStatic())
   StaticVar( Foo, ‘Step’, CalculateStep())
   Foo.Var += Foo.Step
   print(‘Value of Var : ‘, Foo.Var)

然后,我这样写了“ StaticVar”:

  def StaticVar(Cls, Var, StaticVal) :
     if not hasattr(Cls, Var) :
        setattr(Cls, Var, StaticVal)

我什至可以用Python编写更好的代码:

def StaticVars(Cls, **Vars) :
    for Var, StaticVal in Vars.items() :
        if not hasattr(Cls, Var) :
            setattr(Cls, Var, StaticVal)

  def  Foo () :
      StaticVars( Foo, Var = CalculateStatic(),Step= CalculateStep()))
      Foo.Var += Foo. Step
      print(‘Value of Var : ‘, Foo.Var)

当然,这是编写代码的一种好方法,但是我的目标(仅一次调用初始化函数)没有达到(只需在初始化函数中添加打印以查看它经常被调用)!事实是,在函数调用中,甚至在调用函数之前都会评估参数值。

def CalculateStatic() :
    print("Costly Initialization")
    return 0

def CalculateStep() :
    return 2

def Test() :
    Foo()
    Foo()
    Foo()

>>> Test()
Costly Initialization
Value of Var : 2
Costly Initialization
Value of Var : 4
Costly Initialization
Value of Var : 6

为了达到我的目标,我宁愿这样写:

def  Foo () :
    if not hasattr(Foo, ‘Var’) :
        setattr ( Foo, ‘Var’, CalculateStatic())
        setattr ( Foo, ‘Step’, CalculateStep())

    Foo.Var += Foo. Step
    print(‘Value of Var : ‘, Foo.Var)

>>> Test()
Costly Initialization
Value of Var : 2
Value of Var : 4
Value of Var : 6

它可能像这样“写得很好”(我使用下划线表示“ private == static”):

def StaticVars(Cls, **Vars) :
    for Var, StaticVal in Vars.items() :
        setattr(Cls, Var, StaticVal)

def  Foo () :
    _ = Foo
    try :
        __ = _.Var
    except AttributeError : # The above code could only generate AttributeError Exception
                    # the following code is executed only once
        StaticDefVars(_, Var= CalculateStatic(), Step = CalculateStep())

    _.Var += _. Step
    print(‘Value of Var : ‘, Foo.Var)

必须注意不要将“计算代码”放在“ try”子句中,这可能会产生额外的“ AttributeError”异常。

当然,如果Python具有“ Marcro预处理”功能,那就更好了 “'