gpt4 book ai didi

python - 如何在不明确接受的情况下让自己进入 Python 方法

转载 作者:太空狗 更新时间:2023-10-29 20:39:11 24 4
gpt4 key购买 nike

我正在开发一个文档测试框架——基本上是 PDF 的单元测试。测试是由框架定义的类实例的(修饰的)方法,这些方法在运行时被定位和实例化,并且调用这些方法来执行测试。

我的目标是减少编写测试的人员需要关注的古怪 Python 语法的数量,因为这些人可能是也可能不是 Python 程序员,甚至根本不是程序员。所以我希望他们能够为方法编写“def foo():”而不是“def foo(self):”,但仍然能够使用“self”来访问成员。

在普通程序中,我认为这是一个可怕的想法,但在像这样的特定领域语言程序中,似乎值得一试。

我已经通过使用装饰器成功地从方法签名中消除了 self(实际上,因为我已经在测试用例中使用了装饰器,所以我会把它放入其中),但是“self”并不指代测试用例方法中的任何内容。

我考虑过为自己使用全局变量,甚至想出了一个或多或少可行的实现,但我宁愿污染尽可能小的命名空间,这就是为什么我更愿意将变量直接注入(inject)测试case 方法的本地命名空间。有什么想法吗?

最佳答案

我对这个问题的接受答案非常愚蠢,但我才刚刚开始。这是一个更好的方法。这只是经过了少量测试,但它有利于演示正确的方法来做这件不正确的事情。它肯定适用于 2.6.5。我没有测试过任何其他版本,但没有将操作码硬编码到其中,因此它应该与大多数其他 2.x 代码一样可移植。

add_self 可以用作装饰器,但这会破坏目的(为什么不直接键入“self”?)从我的其他答案中改编元类来应用此函数很容易.

import opcode
import types



def instructions(code):
"""Iterates over a code string yielding integer [op, arg] pairs

If the opcode does not take an argument, just put None in the second part
"""
code = map(ord, code)
i, L = 0, len(code)
extended_arg = 0
while i < L:
op = code[i]
i+= 1
if op < opcode.HAVE_ARGUMENT:
yield [op, None]
continue
oparg = code[i] + (code[i+1] << 8) + extended_arg
extended_arg = 0
i += 2
if op == opcode.EXTENDED_ARG:
extended_arg = oparg << 16
continue
yield [op, oparg]


def write_instruction(inst):
"""Takes an integer [op, arg] pair and returns a list of character bytecodes"""
op, oparg = inst
if oparg is None:
return [chr(op)]
elif oparg <= 65536L:
return [chr(op), chr(oparg & 255), chr((oparg >> 8) & 255)]
elif oparg <= 4294967296L:
# The argument is large enough to need 4 bytes and the EXTENDED_ARG opcode
return [chr(opcode.EXTENDED_ARG),
chr((oparg >> 16) & 255),
chr((oparg >> 24) & 255),
chr(op),
chr(oparg & 255),
chr((oparg >> 8) & 255)]
else:
raise ValueError("Invalid oparg: {0} is too large".format(oparg))


def add_self(f):
"""Add self to a method

Creates a new function by prepending the name 'self' to co_varnames, and
incrementing co_argcount and co_nlocals. Increase the index of all other locals
by 1 to compensate. Also removes 'self' from co_names and decrease the index of
all names that occur after it by 1. Finally, replace all occurrences of
`LOAD_GLOBAL i,j` that make reference to the old 'self' with 'LOAD_FAST 0,0'.

Essentially, just create a code object that is exactly the same but has one more
argument.
"""
code_obj = f.func_code
try:
self_index = code_obj.co_names.index('self')
except ValueError:
raise NotImplementedError("self is not a global")

# The arguments are just the first co_argcount co_varnames
varnames = ('self', ) + code_obj.co_varnames
names = tuple(name for name in code_obj.co_names if name != 'self')

code = []

for inst in instructions(code_obj.co_code):
op = inst[0]
if op in opcode.haslocal:
# The index is now one greater because we added 'self' at the head of
# the tuple
inst[1] += 1
elif op in opcode.hasname:
arg = inst[1]
if arg == self_index:
# This refers to the old global 'self'
if op == opcode.opmap['LOAD_GLOBAL']:
inst[0] = opcode.opmap['LOAD_FAST']
inst[1] = 0
else:
# If `self` is used as an attribute, real global, module
# name, module attribute, or gets looked at funny, bail out.
raise NotImplementedError("Abnormal use of self")
elif arg > self_index:
# This rewrites the index to account for the old global 'self'
# having been removed.
inst[1] -= 1

code += write_instruction(inst)

code = ''.join(code)

# type help(types.CodeType) at the interpreter prompt for this one
new_code_obj = types.CodeType(code_obj.co_argcount + 1,
code_obj.co_nlocals + 1,
code_obj.co_stacksize,
code_obj.co_flags,
code,
code_obj.co_consts,
names,
varnames,
'<OpcodeCity>',
code_obj.co_name,
code_obj.co_firstlineno,
code_obj.co_lnotab,
code_obj.co_freevars,
code_obj.co_cellvars)


# help(types.FunctionType)
return types.FunctionType(new_code_obj, f.func_globals)



class Test(object):

msg = 'Foo'

@add_self
def show(msg):
print self.msg + msg


t = Test()
t.show('Bar')

关于python - 如何在不明确接受的情况下让自己进入 Python 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3453976/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com