为什么我的本地人没有在rof之外更新?

2024-04-27 03:30:44 发布

您现在位置:Python中文网/ 问答频道 /正文

在尝试在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的,但是有人能确定是什么导致了这个问题以及如何修复它吗?你知道吗


Tags: inselfidforinitdefcontextemployee
1条回答
网友
1楼 · 发布于 2024-04-27 03:30:44

在python3中,不可能在locals()中创建新的局部变量,因为在编译时会扣除局部变量集。特别是如果您修改remove_duplicates,使其不具有a = b = int行,Python不会将这些名称视为引用局部变量,而是全局变量。有了这条线,它们就被认为是一个局部变量,是的。你知道吗

另外,通过frame对象更改局部变量是不可能的,因为在python3中,局部变量不再存储在字典中。相反,在cpython3上,frame.f_locals访问使用PyFrame_FastToLocals创建变量的副本,但通常是单向的。因此,虽然您可以读取变量的值,但不会传播任何更改,ab继续is int。但是(module)全局变量仍然存储在一个字典中,该字典可以通过frame.f_globals直接访问;并且字典是开放的,可以进行更改。你知道吗

然而,PyDev维护人员对如何在cpython3上实现这一点还有a blog post。因此,下面的rof实现似乎为我实现了技巧:

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):
        ...

相关问题 更多 >