简体   繁体   English

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

[英]C-like Static Variable inside a Python class method

After 20 years of C++ experience I am struggling to learn something of Python. 经过20年的C ++经验,我努力学习Python。

Now I'd like to have a method (a function inside a class) that has a "static" variable of its own, and not a static class variable. 现在,我想拥有一个方法(类中的函数),该方法具有自己的“静态”变量,而不是静态变量。

Probably a pseudo code example can illustrate better what I want. 伪代码示例可能可以更好地说明我想要的内容。

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

NOTE 1: I used @staticmethod just for simplicity, but this is irrelevant. 注意1:我只是为了简单起见使用@staticmethod ,但这无关紧要。

NOTE 2: This crashes with AttributeError: 'staticmethod' object has no attribute 'counter' but as I said above, this is a pseudo code to clarify my objective. 注2:这崩溃与AttributeError: 'staticmethod' object has no attribute 'counter'但是正如我上面所说,这是一个伪代码,用于阐明我的目标。

I have already learned that this works outside a class: 我已经知道这课堂之外有效

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

But the same trick doesn't seem to work for member-functions. 但是,相同的技巧似乎不适用于成员函数。

Last minute information, I am restricted to using Python 2.7 (not my choice). 最后一分钟的信息,我只能使用Python 2.7(不是我的选择)。

Is there any legal and reliable way to have a persistent variable (or constant) with scope restricted to the member-function scope? 是否有任何合法且可靠的方法来使作用域限于成员函数作用域的持久变量(或常数)?

Some relevant links 一些相关链接

Thanks in advance. 提前致谢。

No, there is not. 不,那里没有。 You've already found the Python version: a class variable that you, the supreme overlord of class dummy development, will access only within function foo . 您已经找到了Python版本:类dummy开发的最高霸主您只能在函数foo访问的类变量。

If it would help to know the rationale for this, you can start that path here . 如果可以帮助您了解这样做的原理,则可以从此处开始。 I expect that you've already been through much of this; 我希望您已经经历了很多事情; however, this answer gives Python specifics for more Pythonic ways to implement what you need. 但是,此答案为Python提供了更多细节,以实现您所需的更多Pythonic方式。

As @Prune already mentioned there is no real way of doing so. 正如@Prune已经提到的那样,没有真正的方法。

However, if you want the static variable inside a method to be available only to the object it belongs to (as it is in C++ as far as I remember), you should define it in the constructor or as a class variable with a non-static method: 但是,如果您希望方法内的静态变量仅可用于其所属的对象(据我所知,就象在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))

or: 要么:

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

    _foo_counter = 0

This way, running: 这样,运行:

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

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

Results in: 结果是:

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.

Note that the two versions do not behave in exactly the same way. 请注意,这两个版本的行为方式并不完全相同。 When you define _foo_counter in the class directly, you will have access to the _foo_counter variable both for the object ( self._foo_counter ) and for the class itself ( dummy._foo_counter ). 当直接在类中定义_foo_counter时,您将可以同时访问对象( self._foo_counter )和类本身( dummy._foo_counter )的_foo_counter变量。 The dummy._foo_counter will be static for every use of the class and will persist across multiple instances of the class, so across multiple objects. 对于每次使用该类, dummy._foo_counter都是静态的,并且将在该类的多个实例之间(因此在多个对象之间)保持不变。 This is also the only variable that you can access if you use the @staticmethod decorator on dummy.foo() : 如果在dummy.foo()上使用@staticmethod装饰器,这也是您可以访问的唯一变量:

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

    _foo_counter = 0

Here, self or _foo_counter will not be accessible, and your only option is to use the class-wide variable dummy._foo_counter (which, as already mentioned, you could use with methods not decorated with @staticmethod as well). 在这里, self_foo_counter将无法访问,并且您唯一的选择是使用类范围的变量dummy._foo_counter (如前所述,您也可以将其与未使用@staticmethod装饰的方法一起使用)。

So that running again: 这样再次运行:

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

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

results in: 结果是:

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.

One way to achieve this is to tuck your variable away in a closure, so it will effectively be static for your purposes. 实现此目的的一种方法是将变量隐藏在一个闭包中,因此对于您的目的而言,它实际上是静态的。 Unfortunately, Python 2 does not support the nonlocal keyword, so we have to wrap our variable's value in an object (unless you only mean to reference and not mutate the variable (ie assign to the variable) in the method: 不幸的是,Python的2不支持nonlocal关键字,所以我们有一个对象来包装我们的变量的值(除非你只意味着引用发生变异的变量(即分配给该方法的变量):

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.

Of course, this is a lot of rigamarole simply to avoid using an instance variable. 当然,只是为了避免使用实例变量,这是很多麻烦的事情。 Perhaps the best solution is to make your method a custom object, and you can implement the descriptor protocol to make it callable as a method, and it will be usable as an instance method if you'd like: 也许最好的解决方案是使您的方法成为自定义对象,并且您可以实现描述符协议以使其可作为方法调用,并且如果您愿意,它将用作实例方法:

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

All of this would be highly irregular and confusing to someone else who is used to python conventions, although I hope you see, the Python data-model offers a lot of power to customize things. 所有这些都是高度不规则的,并且会使习惯于python约定的其他人感到困惑,尽管我希望您看到,Python数据模型提供了许多自定义功能。

note, i've used this as the name of the first argument to avoid confusion with self that will actually come from the object that Foo get's bound to as a method. 注意,我已经使用this作为第一个参数的名称,以避免与混乱self实际上将来自该对象Foo获得的绑定到一种方法。

Again, I should reiterate, I would never do this. 我再次重申,我永远不会这样做。 I would just use an instance variable, or perhaps a generator if your function needs to maintain state, and could be used as an iterator. 如果您的函数需要维护状态,则可以使用实例变量,或者使用生成器,并且可以将其用作迭代器。

Using a mutable type as the default value for a keyword argument for your function is maybe the simplest approach: 使用可变类型作为函数关键字参数的默认值可能是最简单的方法:

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

The rationale is that this variable is initialized only once; 理由是此变量仅初始化一次; its latest value remains in the closure formed. 它的最新价值保留在形成的封盖中。

I already posted this in an old post, but nobody noticed it 我已经在旧帖子中发布了此帖子,但没人注意到它

As I have a different idiomatic objective with static variables, I would like to expose the following: In a function, I want to initialize a variable only once with a calculated value which may be a bit costly. 由于我对静态变量有不同的惯用目标,因此我想介绍以下内容:在一个函数中,我只想使用计算值初始化一次变量,而这可能会有点昂贵。 As I love nice-writing, and being an old C-style programmer. 因为我喜欢出色的写作,并且是一名老的C风格程序员。 I tried to define a macro-like writing: 我试图定义类似宏的文字:

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

Then, I wrote 'StaticVar' like this: 然后,我这样写了“ StaticVar”:

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

I can even write nicer code in Python: 我什至可以用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)

Sure, this is a nice way to write the code, but my objective (only one call of initialization functions) is not met (just add a print in the initialization function to see that the it is called often) ! 当然,这是编写代码的一种好方法,但是我的目标(仅一次调用初始化函数)没有得到满足(只需在初始化函数中添加打印以查看它经常被调用)! The fact is that, in a function call, the parameter value is evaluated even before the function is called. 事实是,在函数调用中,甚至在调用函数之前都会评估参数值。

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

To meet my objective, I'd rather write something like this: 为了实现我的目标,我宁愿这样写:

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

And it could be “nicely written” like this (I used the underscore notation refering to “private == static”): 它可能像这样“写得很好”(我用下划线表示“ 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)

Attention must be paid to not put 'calculation code' in the 'try' clause which could generate extra 'AttributeError' exception. 必须注意不要将“计算代码”放在“ try”子句中,这可能会产生额外的“ AttributeError”异常。

Sure, if Python had had 'Marcro preprocessing', it would be even nicer "' 当然,如果Python具有“ Marcro预处理”功能,那就更好了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM