0

我一直在尝试为我的程序实现 __import__() 和 reload() ,但未能成功。我有一个字符串,我将其写入 .py 文件(模块),然后将其作为模块加载。然后我对该 .py 文件(模块)进行更改并写入它,但我似乎无法在新更新的模块中更改返回值。这是我的代码:

str1 = '''
def run():
    return 10

'''
f = open('mycode.py','w')
f.write(str1)
f.close()
mymodule = __import__('mycode')

es = 'number = mymodule.run()'
exec(es)
print "number", number


str2 = '''
def run():
    return 99

'''
f = open('mycode.py','w')
f.write(str2)
f.close()

mymodule = reload(mymodule)
mymodule = __import__('mycode')

es = 'number = mymodule.run()'
exec(es)
print "number", number


OUTPUT:
>> number 10
>> number 10 # should be 99

我看过这里:Re-import the same python module with __import__() in a fail-safe way

这里:

重新加载已导入另一个模块的模块

和这里:

如何卸载(重新加载)Python 模块?

但我无法想出解决方案。任何帮助,将不胜感激。谢谢。

保罗

编辑

我使用 exec(es) 的原因是因为如果我有多个参数,我想自定义 es。这是一个例子:

p = [2,1]
p2 = [3,4,5]
p3 = [100,200,300,500]

str1 = '''
def run(x,y):
        return x + y

'''

with open('mycode.py','w') as f:
    f.write(str)
import mycode as mymodule

# how do i do this dynamically, 
# how to handle it when # of paremeters for run() change?
print mymodule.run(p[0],p[1])  # hard-coded parameter passing
# or, print mymodule.run(p[0],p3[1],p[2]) # if run() is changed

所以问题是我的 run() 可以有不同的参数。它可以是 run(x,y) 或 run(larry, moe, curly, hickory, dickory, dock)。如何动态地将多个参数传递给 run()?谢谢。

4

2 回答 2

0

您在这里的实际问题非常简单。我将解释如何解决它。但实际上,你不应该这样做。无论你想做什么,你都做错了。

并不是说没有理由任何人都可能想做这样的事情。但我非常有信心,exec因为不知道您可以将函数作为一等值传递、__import__使用文字模块名称等调用而使用的任何人都没有这样的理由。如果你解释了你实际上想要做什么,我们可以解释它。

但与此同时:

当您执行第一个import时,它正在创建一个mycode.pyc文件,并且reload只是重新加载该文件。

您可以通过删除来验证这一点mycode.pyc,然后运行python -B reloadtest.py而不是python reloadtest.py. 这显然为您提供了一种可能的解决方法。

或者,您可以os.unlink('mycode.pyc')在调用reload. 我认为大多数人会认为这非常荒谬,但我认为这与您最初尝试做的事情一样。

最后,如果您创建一个新mycode.py文件而不是就地重写现有文件,我认为这将解决问题。


同时,您的代码与以下代码完全相同。(为了解决您在评论中的困惑:完全相同意味着它将有完全相同的问题,并且完全相同的解决方案将解决它。)它也更简单,更pythonic,更容易调试,因为它只涉及一个巨大的反模式(在交互式解释器之外的任何地方使用重新加载)而不是四个:

str1 = '''
def run():
    return 10

'''
with open('mycode.py', 'w') as f:
    f.write(str1)
import mycode as mymodule

print "number", mymodule.run()

str2 = '''
def run():
    return 99

'''
with open('mycode.py', 'w') as f:
    f.write(str2)

reload(mymodule)

print "number", mymodule.run()

在您的编辑中,您解释说您正在使用exec,因为您想传递动态可变数量的参数:

它可以是 run(x,y) 或 run(larry, moe, curly, hickory, dickory, dock)。如何动态地将多个参数传递给 run()?

简单的。只需将x, yorlarry, moe, curly, hickory, dickory, dock放入tuple,list或其他序列中,然后使用run(*seq).

例如,我假设你有这样的代码:

es = 'number = mymodule.run({})'.format(','.join(params))
exec(es)

您可以将其替换为:

number = mymodule.run(*params)

事实上,几乎任何情况下你都可以想象你认为你需要的地方exec,有更好的方法来做到这一点。如果您无法弄清楚,您可以在 Google 或 StackOverflow 上进行快速搜索,如果失败了,只需询问如何操作即可。用于动态代码生成对于tclexec来说是惯用的,并且在几年前曾经用于 PHP 和 JavaScript,但在 Python 中几乎从来都不是正确的做法。

而且,您现在可能已经猜到了,动态创建模块到import/// etc也是如此reloadexecfile

例如,您的真实代码可能正在尝试执行以下操作:

def make_new_module(n):
    str = '''
    def run():
        return {}        
    '''.format(n)
    f = open('mycode.py','w')
    f.write(str1)
    f.close()
    return __import__('mycode')

但是您可以动态定义函数:

def make_new_function(n):
    def run():
        return n
    return run

如果你真的需要命名mymodule.run,你可以轻松地创建一个命名空间,如类、类实例namedtuple等,将其粘贴进去。

如果你真的需要,你甚至可以即时创建一个新模块(尽管我敢打赌你不需要):

import types
def make_new_module(n):
    def run():
        return n
    mymodule = types.ModuleType('mymodule')
    mymodule.run = run
    return mymodule

在现有模块中重新绑定函数更容易:

def change_mymodule_run(n):
    def run():
        return n
    mymodule.run = run

是的,模块是一等值,就像函数一样,所以如果module是动态参数而不是静态值,你可以很容易地做到这一点:

def change_module_run(module, n):
    def run():
        return n
    module.run = run
于 2013-03-09T00:40:40.053 回答
0

正如一些评论者所说,您可能不需要编写模块来获得动态代码。这是一个替代实现,它创建了一个run函数的两个不同版本,它们给出了不同的结果:

def run_function_factory(n):
    def run_function():
        return n
    return run_function

run1 = run_function_factory(10)
print("run1 returns", run1()) # prints "run1 returns 10"

run2 = run_function_factory(99)
print("run2 returns", run2()) # prints "run2 returns 99"

显然,您的实际代码会更复杂,但是对于您需要的几乎任何变化,都应该可以使用类似的编码风格(将函数视为一流对象)。例如,如果run接受两个参数,a并对b它们进行一些算术运算,然后n在返回之前,你会使用这样的东西:

def run_function_factory(n):
    def run_function(a, b):
        return a*n + b

run1 = run_function_factory(10)
for x in range(3):
    for y in range(2,5):
        print(run1(x,y)) # prints 2,3,4,12,13,14,22,23,24 (one per line)
于 2013-03-09T01:19:18.973 回答