[Python] 参照渡し

見るも無惨なキモイコードシリーズ
というか引数を内部でポンポンいじられるのって単純な話分かりにくいよ。関数で加工した値を再度代入する、という形はやっぱり分かりやすい。

import sys
from opcode import *


def callee_names(frame_depth=1):

    result = []

    frame = sys._getframe(frame_depth + 2)
    co = frame.f_code
    code = co.co_code

    i = 0; n = frame.f_lasti - 2
    oparg = extended_arg = 0

    while i < n:
        c = code[i]
        op = ord(c)

        i = i + 1
        if op >= HAVE_ARGUMENT:
            oparg = ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg
            extended_arg = 0
            i = i + 2
            if op == EXTENDED_ARG:
                extended_arg = oparg * 65536L
        if op in hasconst:
            result.append(co.co_consts[oparg])
        elif op in hasname:
            result.append(co.co_names[oparg])
        elif op in haslocal:
            result.append(co.co_varnames[oparg])
        elif op in hasfree:
            result.append((co.co_cellvars + co.co_freevars)[oparg])

    callee = sys._getframe(frame_depth + 1)
    argcount = callee.f_code.co_argcount
    index = argcount

    if callee.f_code.co_flags & 0x04:
        argcount += len(callee.f_locals[callee.f_code.co_varnames[index]])
        index += 1

    if callee.f_code.co_flags & 0x08:
        argcount += len(callee.f_locals[callee.f_code.co_varnames[index]]) * 2

    result = result[-argcount:]
    return result


class ByRef(object):

    def __init__(self):
        self.__dict__['locals'] = callee_names(1)

    def __setattr__(self, _name, _value):

        callee = sys._getframe(1)
        caller = sys._getframe(2)

        argindex = list(callee.f_code.co_varnames).index(_name)
        localname = self.__dict__['locals'][argindex]

        caller.f_locals[localname] = _value


if __name__ == '__main__':

    def foo(v1, v2, *args, **kwargs):
        byref = ByRef() # first of all, create ByRef instance.
        byref.v2 = v1 * 2

    a = 10
    b = 0
    c = -10
    print b
    foo(a, b, c,
        foo=1)
    print b

例によって動かない可能性がある