尝试在Python中实现C-for循环后,开发了以下函数:
import sys
def rof(init, cond, post):
init, cond, post, context = compile(init, '<rof>', 'exec'), \
compile(cond, '<rof>', 'eval'), \
compile(post, '<rof>', 'exec'), \
sys._getframe(1)
context = context.f_globals, context.f_locals
exec(init, *context)
while eval(cond, *context):
yield None
exec(post, *context)
正如任何程序员所知,新功能需要进行测试以确保其有效:
设置
class Employee:
def __init__(self, employee_id, category, hired, salary, years):
vars(self).update(locals())
def __repr__(self):
return '{}({})'.format(self.__class__.__name__,
', '.join(map(repr, self)))
def __iter__(self):
yield self.employee_id
yield self.category
yield self.hired
yield self.salary
yield self.years
database = [Employee(123, 'P', 2014, 2000, 0),
Employee(234, 'F', 2000, 20000, 14),
Employee(123, 'F', 2010, 10000, 4)]
在某些情况下(例如下面的代码),代码运行时没有错误:
试用1
for _ in rof('a = 0', 'a < len(database)', 'a += 1'):
employee_id = database[a].employee_id
for _ in rof('b = len(database) - 1', 'b > a', 'b -= 1'):
if database[b].employee_id == employee_id:
print(database[b], 'is being removed.')
del database[b]
但是,当循环处于单独的函数中时,它不起作用。
试用2
def remove_duplicates(database):
a = b = int
for _ in rof('a = 0', 'a < len(database)', 'a += 1'):
employee_id = database[a].employee_id
for _ in rof('b = len(database) - 1', 'b > a', 'b -= 1'):
if database[b].employee_id == employee_id:
print(database[b], 'is being removed.')
del database[b]
remove_duplicates(database)
改为生成错误(TypeError: list indices must be integers, not type
)。
我们都同意这个代码不是Pythonic,但任何人都可以确定导致问题的原因以及如何修复它?
答案 0 :(得分:3)
在Python 3中,无法在locals()
中创建新的局部变量,因为在编译时会扣除一组局部变量。特别是如果您修改remove_duplicates
以使其没有a = b = int
行,则Python不会将这些名称视为本地变量而是全局变量。有了该行的存在,它们被认为是一个局部变量,是的。
此外,通过框架对象无法更改局部变量,因为在Python 3中,局部变量不再存储在字典中。相反,在CPython 3上,frame.f_locals
访问使用PyFrame_FastToLocals
创建变量的副本,但它通常是单向行程。因此,虽然您可以读取变量的值,但不会传播任何更改,a
和b
会继续is int
。但是(模块)全局变量仍存储在可通过frame.f_globals
直接访问的字典中;这本词典可供更改。
然而,PyDev维护者有a blog post如何在CPython 3上实现这一点。因此,以下rof
实现似乎为我做了 :< / p>
def apply(frame):
ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame), ctypes.c_int(0))
def rof(init, cond, post):
init, cond, post, context = compile(init, '<rof>', 'exec'), \
compile(cond, '<rof>', 'eval'), \
compile(post, '<rof>', 'exec'), \
sys._getframe(1)
exec(init, context.f_globals, context.f_locals)
apply(context)
while eval(cond, context.f_globals, context.f_locals):
apply(context)
yield None
exec(post, context.f_globals, context.f_locals)
apply(context)
我认为这是代码是令人厌恶的,如果有的话,并建议不是这样,假设的程序员将知道如何将C for循环更改为C while循环...并从那里将其工作到Python。如果没有在函数体内为这些变量赋予初始值,它仍然无法工作。
因此,我提出了另一种rof
实施方案:
def rof(init, cond, post):
print(init)
print('while {}:'.format(cond))
print(' # code goes here')
print(' ' + post)
rof('b = len(database) - 1', 'b > a', 'b -= 1')
打印:
b = len(database) - 1
while b > a:
# code goes here
b -= 1
无论如何都应该写出来。
虽然在这种情况下没有太大的错误:
for a in range(len(database)):
for b in range(len(database) - 1, a, -1):
...