2013-10-01 61 views
1

我试图在我的方法中实现一个所谓的静态变量,类似于decorator method described in this Stackoverflow thread。具体地讲,我限定一个装饰功能如下:类中的函数实例变量

def static_var(varName, value): 
    def decorate(function): 
     setattr(function,varName,value) 
     return function 
    return decorate 

如示例所示,这可以用于一个变量连接到功能:

@static_var('seed', 0) 
def counter(): 
    counter.seed +=1 
    return counter.seed 

此方法将返回的次数它已被调用。

我遇到的问题是,这不,如果我定义一个类里面的方法工作:如果我实例化一个Circle和运行counter

class Circle(object): 

    @static_var('seed',0) 
    def counter(self): 
     counter.seed +=1 
     return counter.seed 

>>>> myCircle = Circle() 
>>>> myCircle.counter() 

我得到的以下错误:NameError: global name 'counter' is not defined

我的回应是,也许我需要使用self.counter,即

class Circle(object): 

    @static_var('seed',0) 
    def counter(self): 
     self.counter.seed +=1 
     return self.counter.seed 

然而,这会产生错误,AttributeError: 'instancemethod' object has no attribute 'seed'

这是怎么回事?

回答

6

你想访问函数对象,但是你正在访问一个方法。 Python将实例和类的函数视为descriptors,在查找时返回绑定方法。

用途:

@static_var('seed',0) 
def counter(self): 
    self.counter.__func__.seed += 1 

达到包装的函数对象。

在Python 3,也可以访问该功能的对象的类:

@static_var('seed',0) 
def counter(self): 
    Circle.counter.seed += 1 

在Python 2中还是会返回一个未结合的方法对象的方法(没有附加的实例)。

当然,只是因为你可以做到这一点,并不一定是一个好主意。用一种方法,你有一个班级,给你一个替代地点来存储该计数器。你可以把它放在Countertype(self),其中后者会给你一个计数器每个子类

+0

啊,我看。我确实尝试过'Circle.counter.seed',但我使用的是Python 2.7,所以当然它不起作用。 – AnjoMan

+0

'Circle.counter .__ func __。seed'也可以。 –

+0

当然,一旦你有一堂课,你也可以将实例存储在实例中并完成。 – Marcin

4

你试图实现的东西看起来像你根本不应该做的事情。

在第一种情况下,你可以很容易地摆脱简单得多:

def counter(): 
    counter.seed += 1 
    return counter 
counter.seed = 0 

而在第二种情况下,你可以很容易地把“功能状态”的类。

class Circle(object): 
    seed = 0 

    # if you want the count to be unique per instance 
    def counter_inst(self): 
     self.seed += 1 
     return self.seed 

    # if you want the count to be shared between all instances of the class 
    @classmethod 
    def counter_cls(cls): 
     cls.seed += 1 
     return cls.seed 
1

我可以提出另一种选择,这可能是更好一点的使用和外观都相同的两种方法及功能:

@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 

如果你喜欢使用,这里的实现:

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 
+0

所以你刚刚重新创造了“自我”! –

+0

@ErikAllik:不完全。它基本上是一种将函数属性提升到函数体中而不必参考函数名称的方法 – Claudiu

+0

我所能看到的是,函数有一个新的'statics'参数,其功能与'self'相同,是的,在语义上你的解释稍有不同,但我没有看到任何实际的区别。 –

2

问题是类方法是descriptor objects,而不是函数。如果你在方法中做了更多的工作,你可以在包含v3.x的Python v2.6中对这两种类型的可调用使用相同的装饰器。这就是我的意思是:

def static_var(var_name, value): 
    def decorator(function): 
     setattr(function, var_name, value) 
     return function 
    return decorator 

# apply it to method 
class Circle(object): 
    @static_var('seed', 0) 
    def counter(self): 
     counter_method = Circle.counter.__get__(self, Circle).__func__ # added 
     counter_method.seed +=1 
     return counter_method.seed 

myCircle = Circle() 
print(myCircle.counter()) # 1 
print(myCircle.counter()) # 2 

什么方法版本所做的就是调用描述符的__get__方法来获得一个绑定方法实例对象,然后访问其__func__属性以获得其具有连接到指定属性的实际功能情况它。

对于2.6之前的Python版本,您需要使用im_func而不是__func__

更新:

大多数注意到可以通过改变装饰,使其增加了一个参数调用的开始和写作装饰功能来指代,而不是自己要避开的问题访问变量。另一个好处是,这种方法在Python 2.x和3.x两个工程:

def static_var(var_name, value): 
    def decorator(function): 
     static_vars = getattr(function, 'static_vars', None) 
     if static_vars: # already have a container? 
      setattr(static_vars, var_name, value) # add another var to it 
      return function 
     else: 
      static_vars = type('Statics', (object,), {})() # create container 
      setattr(static_vars, var_name, value) # add first var to it 
      def decorated(*args, **kwds): 
       return function(static_vars, *args, **kwds) 
      decorated.static_vars = static_vars 
      return decorated 
    return decorator 

@static_var('seed', 0) # apply it to a function 
def counter(static_vars): 
    static_vars.seed +=1 
    return static_vars.seed 

print(counter()) # 1 
print(counter()) # 2 

class Circle(object): 
    @static_var('seed', 0) # apply it to a method 
    def counter(static_vars, self): 
     static_vars.seed +=1 
     return static_vars.seed 

myCircle = Circle() 
print(myCircle.counter()) # 1 
print(myCircle.counter()) # 2 

这个装饰允许添加多个静态:

@static_var('seed', 0) # add two of them to a function 
@static_var('offset', 42) 
def counter2(static_vars): 
    static_vars.seed += 1 
    static_vars.offset *= 2 
    return static_vars.seed + static_vars.offset 

print(counter2()) # 1 + 2*42 = 85 
print(counter2()) # 2 + 2*84 = 170