简体   繁体   English

function 中的 static 变量的 Python 是什么?

[英]What is the Python equivalent of static variables inside a function?

What is the idiomatic Python equivalent of this C/C++ code?此 C/C++ 代码的惯用 Python 等价物是什么?

void foo()
{
    static int counter = 0;
    counter++;
    printf("counter is %d\n", counter);
}

specifically, how does one implement the static member at the function level, as opposed to the class level?具体来说,如何在 function 级别而不是 class 级别实施 static 成员? And does placing the function into a class change anything?将 function 放入 class 会改变什么吗?

A bit reversed, but this should work:有点颠倒,但这应该有效:

def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter
foo.counter = 0

If you want the counter initialization code at the top instead of the bottom, you can create a decorator:如果您希望计数器初始化代码位于顶部而不是底部,则可以创建一个装饰器:

def static_vars(**kwargs):
    def decorate(func):
        for k in kwargs:
            setattr(func, k, kwargs[k])
        return func
    return decorate

Then use the code like this:然后使用这样的代码:

@static_vars(counter=0)
def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter

It'll still require you to use the foo.它仍然需要您使用foo. prefix, unfortunately.前缀,不幸的是。

(Credit: @ony ) (信用: @ony

You can add attributes to a function, and use it as a static variable.您可以向函数添加属性,并将其用作静态变量。

def myfunc():
  myfunc.counter += 1
  print myfunc.counter

# attribute must be initialized
myfunc.counter = 0

Alternatively, if you don't want to setup the variable outside the function, you can use hasattr() to avoid an AttributeError exception:或者,如果您不想在函数外设置变量,您可以使用hasattr()来避免AttributeError异常:

def myfunc():
  if not hasattr(myfunc, "counter"):
     myfunc.counter = 0  # it doesn't exist yet, so initialize it
  myfunc.counter += 1

Anyway static variables are rather rare, and you should find a better place for this variable, most likely inside a class.无论如何,静态变量是相当少见的,你应该为这个变量找到一个更好的地方,最有可能是在一个类中。

One could also consider:还可以考虑:

def foo():
    try:
        foo.counter += 1
    except AttributeError:
        foo.counter = 1

Reasoning:推理:

  • much pythonic ("ask for forgiveness not permission")很多pythonic(“请求宽恕而不是许可”)
  • use exception (thrown only once) instead of if branch (think StopIteration exception)使用异常(只抛出一次)而不是if分支(想想StopIteration异常)

Many people have already suggested testing 'hasattr', but there's a simpler answer:许多人已经建议测试“hasattr”,但有一个更简单的答案:

def func():
    func.counter = getattr(func, 'counter', 0) + 1

No try/except, no testing hasattr, just getattr with a default.没有try/except,没有测试hasattr,只是getattr 使用默认值。

Other answers have demonstrated the way you should do this.其他答案已经证明了您应该这样做的方式。 Here's a way you shouldn't:这里有一种你不应该的方式:

>>> def foo(counter=[0]):
...   counter[0] += 1
...   print("Counter is %i." % counter[0]);
... 
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>> 

Default values are initialized only when the function is first evaluated, not each time it is executed, so you can use a list or any other mutable object to store static values.默认值仅在第一次计算函数时初始化,而不是每次执行时初始化,因此您可以使用列表或任何其他可变对象来存储静态值。

Here is a fully encapsulated version that doesn't require an external initialization call:这是一个完全封装的版本,不需要外部初始化调用:

def fn():
    fn.counter=vars(fn).setdefault('counter',-1)
    fn.counter+=1
    print (fn.counter)

In Python, functions are objects and we can simply add, or monkey patch, member variables to them via the special attribute __dict__ .在 Python 中,函数是对象,我们可以通过特殊属性__dict__简单地向它们添加或猴子补丁成员变量。 The built-in vars() returns the special attribute __dict__ .内置vars()返回特殊属性__dict__

EDIT: Note, unlike the alternative try:except AttributeError answer, with this approach the variable will always be ready for the code logic following initialization.编辑:注意,与替代try:except AttributeError答案不同,使用这种方法,变量将始终为初始化后的代码逻辑做好准备。 I think the try:except AttributeError alternative to the following will be less DRY and/or have awkward flow:我认为try:except AttributeError替代以下内容将不那么 DRY 和/或有尴尬的流程:

def Fibonacci(n):
   if n<2: return n
   Fibonacci.memo=vars(Fibonacci).setdefault('memo',{}) # use static variable to hold a results cache
   return Fibonacci.memo.setdefault(n,Fibonacci(n-1)+Fibonacci(n-2)) # lookup result in cache, if not available then calculate and store it

EDIT2: I only recommend the above approach when the function will be called from multiple locations. EDIT2:当从多个位置调用函数时,我只推荐上述方法。 If instead the function is only called in one place, it's better to use nonlocal :如果该函数仅在一处调用,则最好使用nonlocal

def TheOnlyPlaceStaticFunctionIsCalled():
    memo={}
    def Fibonacci(n):
       nonlocal memo  # required in Python3. Python2 can see memo
       if n<2: return n
       return memo.setdefault(n,Fibonacci(n-1)+Fibonacci(n-2))
    ...
    print (Fibonacci(200))
    ...

Python doesn't have static variables but you can fake it by defining a callable class object and then using it as a function. Python 没有静态变量,但您可以通过定义一个可调用的类对象然后将其用作函数来伪造它。 Also see this answer .另请参阅此答案

class Foo(object):
  # Class variable, shared by all instances of this class
  counter = 0

  def __call__(self):
    Foo.counter += 1
    print Foo.counter

# Create an object instance of class "Foo," called "foo"
foo = Foo()

# Make calls to the "__call__" method, via the object's name itself
foo() #prints 1
foo() #prints 2
foo() #prints 3

Note that __call__ makes an instance of a class (object) callable by its own name.请注意, __call__使类(对象)的实例可通过其自己的名称进行调用。 That's why calling foo() above calls the class' __call__ method.这就是上面调用foo()调用类的__call__方法的原因。 From the documentation :从文档

Instances of arbitrary classes can be made callable by defining a __call__() method in their class.通过在类中定义__call__()方法,可以使任意类的实例可调用。

Use a generator function to generate an iterator.使用生成器函数生成迭代器。

def foo_gen():
    n = 0
    while True:
        n+=1
        yield n

Then use it like然后像这样使用它

foo = foo_gen().next
for i in range(0,10):
    print foo()

If you want an upper limit:如果你想要一个上限:

def foo_gen(limit=100000):
    n = 0
    while n < limit:
       n+=1
       yield n

If the iterator terminates (like the example above), you can also loop over it directly, like如果迭代器终止(如上面的例子),你也可以直接循环遍历它,比如

for i in foo_gen(20):
    print i

Of course, in these simple cases it's better to use xrange :)当然,在这些简单的情况下,最好使用 xrange :)

Here is the documentation on the yield statement .这是关于yield 语句的文档。

Other solutions attach a counter attribute to the function, usually with convoluted logic to handle the initialization.其他解决方案将计数器属性附加到函数,通常使用复杂的逻辑来处理初始化。 This is inappropriate for new code.这不适用于新代码。

In Python 3, the right way is to use a nonlocal statement:在 Python 3 中,正确的方法是使用nonlocal语句:

counter = 0
def foo():
    nonlocal counter
    counter += 1
    print(f'counter is {counter}')

See PEP 3104 for the specification of the nonlocal statement.有关nonlocal语句的规范,请参阅PEP 3104

If the counter is intended to be private to the module, it should be named _counter instead.如果计数器是模块私有的,则应将其命名为_counter

Using an attribute of a function as static variable has some potential drawbacks:使用函数的属性作为静态变量有一些潜在的缺点:

  • Every time you want to access the variable, you have to write out the full name of the function.每次要访问变量时,都必须写出函数的全名。
  • Outside code can access the variable easily and mess with the value.外部代码可以轻松访问该变量并弄乱该值。

Idiomatic python for the second issue would probably be naming the variable with a leading underscore to signal that it is not meant to be accessed, while keeping it accessible after the fact.第二个问题的惯用 python 可能会用前导下划线命名变量,以表明它不打算被访问,同时在事后保持可访问。

Using closures使用闭包

An alternative would be a pattern using lexical closures, which are supported with the nonlocal keyword in python 3.另一种选择是使用词法闭包的模式,python 3 中的nonlocal关键字支持这种模式。

def make_counter():
    i = 0
    def counter():
        nonlocal i
        i = i + 1
        return i
    return counter
counter = make_counter()

Sadly I know no way to encapsulate this solution into a decorator.可悲的是,我不知道如何将此解决方案封装到装饰器中。

Using an internal state parameter使用内部状态参数

Another option might be an undocumented parameter serving as a mutable value container.另一种选择可能是作为可变值容器的未记录参数。

def counter(*, _i=[0]):
    _i[0] += 1
    return _i[0]

This works, because default arguments are evaluated when the function is defined, not when it is called.这是有效的,因为默认参数是在定义函数时计算的,而不是在调用时计算的。

Cleaner might be to have a container type instead of the list, eg更清洁的可能是使用容器类型而不是列表,例如

def counter(*, _i = Mutable(0)):
    _i.value += 1
    return _i.value

but I am not aware of a builtin type, that clearly communicates the purpose.但我不知道一个内置类型,它清楚地传达了目的。

_counter = 0
def foo():
   global _counter
   _counter += 1
   print 'counter is', _counter

Python customarily uses underscores to indicate private variables. Python 通常使用下划线来表示私有变量。 The only reason in C to declare the static variable inside the function is to hide it outside the function, which is not really idiomatic Python.在 C 中在函数内部声明静态变量的唯一原因是将它隐藏在函数之外,这并不是真正惯用的 Python。

def staticvariables(**variables):
    def decorate(function):
        for variable in variables:
            setattr(function, variable, variables[variable])
        return function
    return decorate

@staticvariables(counter=0, bar=1)
def foo():
    print(foo.counter)
    print(foo.bar)

Much like vincent's code above, this would be used as a function decorator and static variables must be accessed with the function name as a prefix.就像上面 vincent 的代码一样,这将用作函数装饰器,并且必须以函数名作为前缀访问静态变量。 The advantage of this code (although admittedly anyone might be smart enough to figure it out) is that you can have multiple static variables and initialise them in a more conventional manner.这段代码的优点(尽管无可否认,任何人都可能足够聪明来弄清楚)是您可以拥有多个静态变量并以更传统的方式初始化它们。

A little bit more readable, but more verbose (Zen of Python: explicit is better than implicit):更具可读性,但更冗长(Python 之禅:显式优于隐式):

>>> def func(_static={'counter': 0}):
...     _static['counter'] += 1
...     print _static['counter']
...
>>> func()
1
>>> func()
2
>>>

See here for an explanation of how this works.有关其工作原理的说明,请参见此处

After trying several approaches I ended up using an improved version of @warvariuc's answer:在尝试了几种方法后,我最终使用了@warvariuc 答案的改进版本:

import types

def func(_static=types.SimpleNamespace(counter=0)):
    _static.counter += 1
    print(_static.counter)

A static variable inside a Python method Python 方法中的静态变量

class Count:
    def foo(self):
        try: 
            self.foo.__func__.counter += 1
        except AttributeError: 
            self.foo.__func__.counter = 1

        print self.foo.__func__.counter

m = Count()
m.foo()       # 1
m.foo()       # 2
m.foo()       # 3

Another (not recommended!) twist on the callable object like https://stackoverflow.com/a/279598/916373 , if you don't mind using a funky call signature, would be to do另一个(不推荐!)对可调用对象的扭曲,如https://stackoverflow.com/a/279598/916373 ,如果您不介意使用时髦的调用签名,则可以这样做

class foo(object):
    counter = 0;
    @staticmethod
    def __call__():
        foo.counter += 1
        print "counter is %i" % foo.counter

>>> foo()()
counter is 1
>>> foo()()
counter is 2

Soulution n +=1溶液 n +=1

def foo():
  foo.__dict__.setdefault('count', 0)
  foo.count += 1
  return foo.count

A global declaration provides this functionality.全局声明提供了此功能。 In the example below (python 3.5 or greater to use the "f"), the counter variable is defined outside of the function.在下面的示例中(python 3.5 或更高版本使用“f”),计数器变量是在函数之外定义的。 Defining it as global in the function signifies that the "global" version outside of the function should be made available to the function.在函数中将其定义为全局意味着函数外部的“全局”版本应该可用于该函数。 So each time the function runs, it modifies the value outside the function, preserving it beyond the function.因此,每次函数运行时,它都会修改函数外的值,将其保留在函数之外。

counter = 0

def foo():
    global counter
    counter += 1
    print("counter is {}".format(counter))

foo() #output: "counter is 1"
foo() #output: "counter is 2"
foo() #output: "counter is 3"

The idiomatic way is to use a class , which can have attributes.惯用的方法是使用一个,它可以有属性。 If you need instances to not be separate, use a singleton.如果您需要实例不分开,请使用单例。

There are a number of ways you could fake or munge "static" variables into Python (one not mentioned so far is to have a mutable default argument), but this is not the Pythonic, idiomatic way to do it.有多种方法可以将“静态”变量伪造或混入 Python(目前未提及的一种方法是具有可变的默认参数),但这不是Pythonic 惯用的方法。 Just use a class.只需使用一个类。

Or possibly a generator, if your usage pattern fits.或者可能是生成器,如果您的使用模式适合。

Prompted by this question , may I present another alternative which might be a bit nicer to use and will look the same for both methods and functions:这个问题的提示下,我可以提出另一种可能更好用的替代方案,并且对于方法和函数看起来都一样:

@static_var2('seed',0)
def funccounter(statics, add=1):
    statics.seed += add
    return statics.seed

print funccounter()       #1
print funccounter(add=2)  #3
print funccounter()       #4

class ACircle(object):
    @static_var2('seed',0)
    def counter(statics, self, add=1):
        statics.seed += add
        return statics.seed

c = ACircle()
print c.counter()      #1
print c.counter(add=2) #3
print c.counter()      #4
d = ACircle()
print d.counter()      #5
print d.counter(add=2) #7
print d.counter()      #8    

If you like the usage, here's the implementation:如果你喜欢这种用法,这里是实现:

class StaticMan(object):
    def __init__(self):
        self.__dict__['_d'] = {}

    def __getattr__(self, name):
        return self.__dict__['_d'][name]
    def __getitem__(self, name):
        return self.__dict__['_d'][name]
    def __setattr__(self, name, val):
        self.__dict__['_d'][name] = val
    def __setitem__(self, name, val):
        self.__dict__['_d'][name] = val

def static_var2(name, val):
    def decorator(original):
        if not hasattr(original, ':staticman'):    
            def wrapped(*args, **kwargs):
                return original(getattr(wrapped, ':staticman'), *args, **kwargs)
            setattr(wrapped, ':staticman', StaticMan())
            f = wrapped
        else:
            f = original #already wrapped

        getattr(f, ':staticman')[name] = val
        return f
    return decorator

Instead of creating a function having a static local variable, you can always create what is called a "function object" and give it a standard (non-static) member variable.您始终可以创建所谓的“函数对象”并为其提供标准(非静态)成员变量,而不是创建具有静态局部变量的函数。

Since you gave an example written C++, I will first explain what a "function object" is in C++.既然你给出了一个用 C++ 编写的例子,我将首先解释什么是 C++ 中的“函数对象”。 A "function object" is simply any class with an overloaded operator() . “函数对象”只是具有重载operator()任何类。 Instances of the class will behave like functions.类的实例将表现得像函数。 For example, you can write int x = square(5);例如,你可以写int x = square(5); even if square is an object (with overloaded operator() ) and not technically not a "function."即使square是一个对象(带有重载的operator() )并且在技术上不是“函数”。 You can give a function-object any of the features that you could give a class object.您可以为函数对象提供您可以为类对象提供的任何功能。

# C++ function object
class Foo_class {
    private:
        int counter;     
    public:
        Foo_class() {
             counter = 0;
        }
        void operator() () {  
            counter++;
            printf("counter is %d\n", counter);
        }     
   };
   Foo_class foo;

In Python, we can also overload operator() except that the method is instead named __call__ :在 Python 中,我们也可以重载operator()只是该方法被命名为__call__

Here is a class definition:这是一个类定义:

class Foo_class:
    def __init__(self): # __init__ is similair to a C++ class constructor
        self.counter = 0
        # self.counter is like a static member
        # variable of a function named "foo"
    def __call__(self): # overload operator()
        self.counter += 1
        print("counter is %d" % self.counter);
foo = Foo_class() # call the constructor

Here is an example of the class being used:这是正在使用的类的示例:

from foo import foo

for i in range(0, 5):
    foo() # function call

The output printed to the console is:打印到控制台的输出是:

counter is 1
counter is 2
counter is 3
counter is 4
counter is 5

If you want your function to take input arguments, you can add those to __call__ as well:如果你想让你的函数接受输入参数,你也可以将它们添加到__call__中:

# FILE: foo.py - - - - - - - - - - - - - - - - - - - - - - - - -

class Foo_class:
    def __init__(self):
        self.counter = 0
    def __call__(self, x, y, z): # overload operator()
        self.counter += 1
        print("counter is %d" % self.counter);
        print("x, y, z, are %d, %d, %d" % (x, y, z));
foo = Foo_class() # call the constructor

# FILE: main.py - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

from foo import foo

for i in range(0, 5):
    foo(7, 8, 9) # function call

# Console Output - - - - - - - - - - - - - - - - - - - - - - - - - - 

counter is 1
x, y, z, are 7, 8, 9
counter is 2
x, y, z, are 7, 8, 9
counter is 3
x, y, z, are 7, 8, 9
counter is 4
x, y, z, are 7, 8, 9
counter is 5
x, y, z, are 7, 8, 9

This answer builds on @claudiu 's answer.这个答案建立在@claudiu 的答案之上。

I found that my code was getting less clear when I always had to prepend the function name, whenever I intend to access a static variable.我发现每当我打算访问静态变量时,我总是不得不在函数名之前加上函数名,因此我的代码变得不那么清晰了。

Namely, in my function code I would prefer to write:也就是说,在我的函数代码中,我更愿意这样写:

print(statics.foo)

instead of代替

print(my_function_name.foo)

So, my solution is to :所以,我的解决方案是:

  1. add a statics attribute to the function向函数添加一个statics属性
  2. in the function scope, add a local variable statics as an alias to my_function.statics在函数作用域中,添加一个局部变量statics作为my_function.statics的别名
from bunch import *

def static_vars(**kwargs):
    def decorate(func):
        statics = Bunch(**kwargs)
        setattr(func, "statics", statics)
        return func
    return decorate

@static_vars(name = "Martin")
def my_function():
    statics = my_function.statics
    print("Hello, {0}".format(statics.name))

Remark评论

My method uses a class named Bunch , which is a dictionary that supports attribute-style access, a la JavaScript (see the original article about it, around 2000)我的方法使用了一个名为Bunch的类,它是一个支持属性样式访问的字典,一种 JavaScript(参见关于它的原始文章,大约在 2000 年)

It can be installed via pip install bunch它可以通过pip install bunch

It can also be hand-written like so:它也可以像这样手写:

class Bunch(dict):
    def __init__(self, **kw):
        dict.__init__(self,kw)
        self.__dict__ = self

This answer shows that setdefault does not truly satisfy the OPs question of how to create static local variables. 该答案表明setdefault不能真正满足OP如何创建静态局部变量的问题。

def fn():
    fn.counter = vars(fn).setdefault('counter',-1)

It works as long as fn. 它的作用时间只要fn。 is prefixed to each variable name. 在每个变量名之前添加前缀。 If you remove them like so: 如果像这样删除它们:

def fn():
   counter = vars(fn).setdefault('counter',-1)
   counter += 1
   print (counter)

there are no errors but counter is always 0 and that tells me the vars(fn) is NOT accessing local variables but rather a global, probably a decorator or attribute stash. 没有错误,但counter始终为0,这告诉我vars(fn)不是访问局部变量,而是全局的,可能是装饰器或属性存储。

Had this worked it would have been my preferred solution. 如果这项工作奏效,那将是我的首选解决方案。 However since it doesn't, I'm inclined to go with a fully encapsulated class definition to create such static vars. 但是,由于没有,我倾向于使用完全封装的类定义来创建此类静态var。

IMHO that is the most straightforward. 恕我直言,这是最简单的。 Of course it depends on if you are more familiar with functional vs. OOP coding styles. 当然,这取决于您是否更熟悉功能和OOP编码样式。

I personally prefer the following to decorators.我个人更喜欢以下装饰器。 To each their own.各有各的。

def staticize(name, factory):
    """Makes a pseudo-static variable in calling function.

    If name `name` exists in calling function, return it. 
    Otherwise, saves return value of `factory()` in 
    name `name` of calling function and return it.

    :param name: name to use to store static object 
    in calling function
    :type name: String
    :param factory: used to initialize name `name` 
    in calling function
    :type factory: function
    :rtype: `type(factory())`

    >>> def steveholt(z):
    ...     a = staticize('a', list)
    ...     a.append(z)
    >>> steveholt.a
    Traceback (most recent call last):
    ...
    AttributeError: 'function' object has no attribute 'a'
    >>> steveholt(1)
    >>> steveholt.a
    [1]
    >>> steveholt('a')
    >>> steveholt.a
    [1, 'a']
    >>> steveholt.a = []
    >>> steveholt.a
    []
    >>> steveholt('zzz')
    >>> steveholt.a
    ['zzz']

    """
    from inspect import stack
    # get scope enclosing calling function
    calling_fn_scope = stack()[2][0]
    # get calling function
    calling_fn_name = stack()[1][3]
    calling_fn = calling_fn_scope.f_locals[calling_fn_name]
    if not hasattr(calling_fn, name):
        setattr(calling_fn, name, factory())
    return getattr(calling_fn, name)

Using a decorator and a closure使用装饰器和闭包

The following decorator can be used create static function variables.以下装饰器可用于创建静态函数变量。 It replaces the declared function with the return from itself.它用自身的返回值替换声明的函数。 This implies that the decorated function must return a function.这意味着被装饰的函数必须返回一个函数。

def static_inner_self(func):
    return func()

Then use the decorator on a function that returns another function with a captured variable:然后在一个函数上使用装饰器,该函数返回另一个带有捕获变量的函数:

@static_inner_self
def foo():
    counter = 0
    def foo():
        nonlocal counter
        counter += 1
        print(f"counter is {counter}")
    return foo

nonlocal is required, otherwise Python thinks that the counter variable is a local variable instead of a captured variable. nonlocal是必需的,否则 Python 认为counter变量是局部变量而不是捕获的变量。 Python behaves like that because of the variable assignment counter += 1 .由于变量赋值counter += 1 ,Python 的行为与此类似。 Any assignment in a function makes Python think that the variable is local.函数中的任何赋值都会使 Python 认为该变量是局部变量。

If you are not assigning to the variable in the inner function, then you can ignore the nonlocal statement, for example, in this function I use to indent lines of a string, in which Python can infer that the variable is nonlocal :如果你不是在内部函数中赋值给变量,那么你可以忽略nonlocal语句,例如,在这个函数中我用来缩进字符串的行,其中 Python 可以推断出变量是nonlocal

@static_inner_self
def indent_lines():
    import re
    re_start_line = re.compile(r'^', flags=re.MULTILINE)
    def indent_lines(text, indent=2):
        return re_start_line.sub(" "*indent, text)
    return indent_lines

PS There is a deleted answer that proposed the same. PS 有一个已删除的答案提出了相同的建议。 I don't know why the author deleted it.不知道作者为什么删了。 https://stackoverflow.com/a/23366737/195417 https://stackoverflow.com/a/23366737/195417

Building on Daniel's answer (additions):基于丹尼尔的回答(补充):

class Foo(object): 
    counter = 0  

def __call__(self, inc_value=0):
    Foo.counter += inc_value
    return Foo.counter

foo = Foo()

def use_foo(x,y):
    if(x==5):
        foo(2)
    elif(y==7):
        foo(3)
    if(foo() == 10):
        print("yello")


use_foo(5,1)
use_foo(5,1)
use_foo(1,7)
use_foo(1,7)
use_foo(1,1)

The reason why I wanted to add this part is , static variables are used not only for incrementing by some value, but also check if the static var is equal to some value, as a real life example.我想添加这部分的原因是,静态变量不仅用于增加某个值,还用于检查静态变量是否等于某个值,作为现实生活中的例子。

The static variable is still protected and used only within the scope of the function use_foo()静态变量仍然受到保护,仅在函数 use_foo() 的范围内使用

In this example, call to foo() functions exactly as(with respect to the corresponding c++ equivalent) :在此示例中,对 foo() 函数的调用与(相对于相应的 C++ 等效项)完全相同:

stat_c +=9; // in c++
foo(9)  #python equiv

if(stat_c==10){ //do something}  // c++

if(foo() == 10):      # python equiv
  #add code here      # python equiv       

Output :
yello
yello

if class Foo is defined restrictively as a singleton class, that would be ideal.如果类 Foo 被限制性地定义为单例类,那将是理想的。 This would make it more pythonic.这将使它更加pythonic。

However, this is a quiet old post, as I have got a different idiomatic objective, I put the following: 但是,这是一个安静的旧帖子,因为我有一个不同的惯用目标,所以我提出以下内容:

In a function, I want to initialize a variable only once by 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)

Even nicer 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)

This is a nice way to write, but my objective (only one call of initialization functions) is not met (just add a print in the initialization function) ! 这是一种不错的书写方式,但是我的目标(仅一次调用初始化函数)没有达到(只需在初始化函数中添加打印件)! The fact is that, in a function call, the parameter value is evaluated before the function is called. 事实是,在函数调用中,参数值是在调用函数之前进行求值的。

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

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

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)

If Python had had 'Marcro preprocessing', it would be nicer.. 如果Python具有“ Marcro预处理”功能,那就更好了。

I write a simple function to use static variables:我写了一个简单的函数来使用静态变量:

def Static():
    ### get the func object by which Static() is called.
    from inspect import currentframe, getframeinfo
    caller = currentframe().f_back
    func_name = getframeinfo(caller)[2]
    # print(func_name)
    caller = caller.f_back
    func = caller.f_locals.get(
        func_name, caller.f_globals.get(
            func_name
        )
    )
    
    class StaticVars:
        def has(self, varName):
            return hasattr(self, varName)
        def declare(self, varName, value):
            if not self.has(varName):
                setattr(self, varName, value)

    if hasattr(func, "staticVars"):
        return func.staticVars
    else:
        # add an attribute to func
        func.staticVars = StaticVars()
        return func.staticVars

How to use:如何使用:

def myfunc(arg):
    if Static().has('test1'):
        Static().test += 1
    else:
        Static().test = 1
    print(Static().test)

    # declare() only takes effect in the first time for each static variable.
    Static().declare('test2', 1)
    print(Static().test2)
    Static().test2 += 1

Miguel Angelo's self-redefinition solution is even possible without any decorator: Miguel Angelo 的自我重定义解决方案甚至可以在没有任何装饰器的情况下实现:

def fun(increment=1):
    global fun
    counter = 0
    def fun(increment=1):
        nonlocal counter
        counter += increment
        print(counter)
    fun(increment)

fun()    #=> 1
fun()    #=> 2
fun(10)  #=> 12

The second line has to be adapted to get a limited scope:第二行必须进行调整才能获得有限的 scope:

def outerfun():
    def innerfun(increment=1):
        nonlocal innerfun
        counter = 0
        def innerfun(increment=1):
            nonlocal counter
            counter += increment
            print(counter)
        innerfun(increment)

    innerfun()    #=> 1
    innerfun()    #=> 2
    innerfun(10)  #=> 12

outerfun()

The plus of the decorator is that you don't have to pay extra attention to the scope of your construction.装饰器的优点是您不必额外注意构造的 scope。

Sure this is an old question but I think I might provide some update.当然这是一个老问题,但我想我可能会提供一些更新。

It seems that the performance argument is obsolete.性能参数似乎已经过时了。 The same test suite appears to give similar results for siInt_try and isInt_re2.相同的测试套件似乎为 siInt_try 和 isInt_re2 提供了相似的结果。 Of course results vary, but this is one session on my computer with python 3.4.4 on kernel 4.3.01 with Xeon W3550.当然,结果各不相同,但这是我的计算机上的一个会话,在内核 4.3.01 和 Xeon W3550 上使用 python 3.4.4。 I have run it several times and the results seem to be similar.我已经运行了几次,结果似乎很相似。 I moved the global regex into function static, but the performance difference is negligible.我将全局正则表达式移动到静态函数中,但性能差异可以忽略不计。

isInt_try: 0.3690
isInt_str: 0.3981
isInt_re: 0.5870
isInt_re2: 0.3632

With performance issue out of the way, it seems that try/catch would produce the most future- and cornercase- proof code so maybe just wrap it in function随着性能问题的解决,似乎 try/catch 会产生最适合未来和角落案例的代码,所以也许只需将它包装在函数中

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

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