Pythonic way to do static local variables?
Bengt Richter
bokr at oz.net
Wed Apr 27 01:58:07 EDT 2005
On Tue, 26 Apr 2005 10:26:51 -0700, Michael Spencer <mahs at telcopartners.com> wrote:
>Terry Reedy wrote:
>> "Charles Krug" <cdkrug at worldnet.att.net> wrote in message
>> news:7Ujbe.647694$w62.444038 at bgtnsc05-news.ops.worldnet.att.net...
>>
>>>Both of these techniques look promising here.
>>
>>
>> Here a third, the closure approach (obviously not directly tested):
>>
>>
>>
>Just for grins, here's a fourth approach, using the descriptor protocol
>
> >>> def func_with_state(state,a):
> ... state.append(a)
> ... return state
> ...
> >>> f = func_with_state.__get__([])
> >>> f(1)
> [1]
> >>> f(2)
> [1, 2]
> >>> f(3)
> [1, 2, 3]
> >>>
>
For those who don't recognize it, this is the mechanism that binds instances to
methods of their type to make bound methods. It's as if you had given list a
new method and picked up the bound method from an instance like [].func_with_state
>>> def func_with_state(state, a):
... state.append(a)
... return state
...
>>> f = func_with_state.__get__([])
>>> f
<bound method ?.func_with_state of []>
The '?' is because we didn't supply the second argument to __get__, i.e., type([])
>>> f = func_with_state.__get__([], list)
>>> f
<bound method list.func_with_state of []>
>>> f.im_func
<function func_with_state at 0x02EFD614>
>>> func_with_state
<function func_with_state at 0x02EFD614>
For grins, here's a fifth approach (I'm assuming you counted correctly to the fourth ;-)
>>> from ut.presets import presets
>>> @presets(state=[])
... def f(a):
... state.append(a)
... return state
...
>>> f(1)
[1]
>>> f(2)
[1, 2]
>>> f(3)
[1, 2, 3]
This is a straight function, with byte-code hack preset of internal state as specified.
The disassemby shows [1, 2, 3] because that is the object referred to still. What would have
been a global reference got hacked to LOAD_FAST 1 (state) after preset from "constant".
>>> import dis
>>> dis.dis(f)
1 0 LOAD_CONST 1 ([1, 2, 3])
3 STORE_FAST 1 (state)
3 6 LOAD_FAST 1 (state)
9 LOAD_ATTR 1 (append)
12 LOAD_FAST 0 (a)
15 CALL_FUNCTION 1
18 POP_TOP
4 19 LOAD_FAST 1 (state)
22 RETURN_VALUE
To see the initial state, we have to re-decorate another instance of f:
>>>
>>> @presets(state=[])
... def f(a):
... state.append(a)
... return state
...
>>> dis.dis(f)
1 0 LOAD_CONST 1 ([])
3 STORE_FAST 1 (state)
3 6 LOAD_FAST 1 (state)
9 LOAD_ATTR 1 (append)
12 LOAD_FAST 0 (a)
15 CALL_FUNCTION 1
18 POP_TOP
Of course, we could put this inside a factory and pass the desired state
>>> def factory(state):
... @presets(state=state)
... def f(a):
... state.append(a)
... return state
... return f
...
>>> f2 = factory([111,222])
>>> f2('third')
[111, 222, 'third']
>>> dis.dis(f2)
2 0 LOAD_CONST 1 ([111, 222, 'third'])
3 STORE_FAST 1 (state)
4 6 LOAD_FAST 1 (state)
9 LOAD_ATTR 1 (append)
12 LOAD_FAST 0 (a)
15 CALL_FUNCTION 1
18 POP_TOP
5 19 LOAD_FAST 1 (state)
22 RETURN_VALUE
>>> dis.dis(factory('append will fail'))
2 0 LOAD_CONST 1 ('append will fail')
3 STORE_FAST 1 (state)
4 6 LOAD_FAST 1 (state)
9 LOAD_ATTR 1 (append)
12 LOAD_FAST 0 (a)
15 CALL_FUNCTION 1
18 POP_TOP
5 19 LOAD_FAST 1 (state)
22 RETURN_VALUE
Note that the function takes only one argument:
>>> f2
<function f at 0x02EF841C>
>>> f2.func_code.co_argcount
1
>>> f2(10, 'fails')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: f() takes exactly 1 argument (2 given)
Regards,
Bengt Richter
More information about the Python-list
mailing list