热插拔python代码(duck类型函数?)

热插拔python代码(duck类型函数?),python,python-3.x,blender,Python,Python 3.x,Blender,我想这件事想得太久了,还不知道,也许你们中的一些人能帮上忙 我有一个python脚本文件夹,所有脚本都有相同的主体(字面上,我是从shell脚本生成的),但有一个块与所有脚本不同。换言之: Top piece of code (always the same) Middle piece of code (changes from file to file) Bottom piece of code (always the same) 我今天意识到这是一个坏主意,例如,如果我想从顶部或底部更改某

我想这件事想得太久了,还不知道,也许你们中的一些人能帮上忙

我有一个python脚本文件夹,所有脚本都有相同的主体(字面上,我是从shell脚本生成的),但有一个块与所有脚本不同。换言之:

Top piece of code (always the same)
Middle piece of code (changes from file to file)
Bottom piece of code (always the same)
我今天意识到这是一个坏主意,例如,如果我想从顶部或底部更改某些内容,我需要编写一个shell脚本来完成。(并不是说这很难,只是看起来它的代码非常糟糕)

所以我想做的是,有一个外部python脚本,如下所示:

Top piece of code
Dynamic function that calls the middle piece of code (based on a parameter)
Bottom piece of code
然后,文件夹中的每个其他python文件都可以是中间的代码段。然而,普通模块在这里不起作用(除非我弄错了),因为我将从参数中获得需要执行的代码,这将是一个字符串,因此直到运行时我才知道要运行哪个函数

所以我又想出了两个解决方案:

  • 我可以编写一组if语句,一个根据某个参数运行每个脚本。我拒绝了这个,因为它比以前的设计更糟糕
  • 我可以使用:

    命令(sys.argv[0]scriptName.py)

    这将运行脚本,但是调用python来调用python对我来说似乎不是很优雅


  • 那么有人有其他想法吗?谢谢。

    如果您知道函数名是字符串,模块名是字符串,那么您可以这样做

    mod = __import__(module_name)
    fn = getattr(mod, fn_name)
    fn()
    
    然而,普通模块在这里不起作用(除非我弄错了),因为我将从参数中获得需要执行的代码,这将是一个字符串,因此直到运行时我才知道要运行哪个函数

    它将很好地工作-使用内置的,或者,如果你有非常复杂的布局,模块来导入你的脚本。然后你可以通过
    模块来获得函数。例如,

    导入一个模块(如其他答案中所解释的)肯定是一种更简洁的方法,但是如果出于某种原因这不起作用,只要你没有做任何太奇怪的事情,你就可以使用它。它基本上运行另一个文件的内容,就好像它在调用
    exec
    时包含在当前文件中一样。它是Python最接近于许多shell中包含的
    语句的东西。作为最低要求,类似这样的方法应该有效:

    exec(open(filename).read(None))
    

    另一种可能的解决方案是让每个重复文件从主文件导入功能

    from topAndBottom import top, bottom
    top()
    # do middle stuff
    bottom()
    
    这个怎么样

    function do_thing_one():
       pass
    
    function do_thing_two():
       pass
    
    dispatch = { "one" : do_thing_one,
                 "two" : do_thing_two,
               }
    
    # do something to get your string from the command line (optparse, argv, whatever)
    # and put it in variable "mystring"
    
    # do top thing
    f = dispatch[mystring]
    f()
    # do bottom thing
    

    除了已经发布的几个答案外,还可以考虑设计模式:制作一个抽象类,如

    class Base(object):
        def top(self): ...
        def bottom(self): ...
        def middle(self): raise NotImplementedError
        def doit(self):
            self.top()
            self.middle()
            self.bottom()
    
    然后,每个可插入模块生成一个类,该类继承自该
    Base
    ,并且必须用相关代码覆盖
    middle


    对于这种简单的情况,可能并不保证(您仍然需要导入正确的模块来实例化其类并在其上调用
    doit
    ),但仍然值得记住(以及它的许多Pythonic变体,我已经在youtube上的许多技术讲座中详细解释了这一点)对于“可插拔部件”的数量或复杂性不断增长的情况,模板方法(尽管其名称可怕;-)是一种可靠、经过充分验证且高度可扩展的模式[[有时有点过于僵化,但这正是我在许多技术讲座中所提到的——这个问题不适用于这个特定的用例]].

    在创建此脚本之前,您试图解决哪个问题?嗯……好吧,虽然脚本将与其他函数放在同一个文件夹中,但我想init.py文件不会有太大问题。虽然所有脚本所在的文件夹都称为python,但我想知道这是否会成为一个问题……只要目录位于
    sys.path
    中,就不应该是一个问题。啊,是的,它工作得很好。我所做的是将所有文件更改为只有一个名为func()的函数,然后使用mod=\uuuu import\uuuu(file\u name\u减号\u py)获得模块名,并调用mod.func(),谢谢。@Leif Andersen,制作一个模块函数接口,每个可用的中间模块都应该实现,他们都应该定义一个名为
    process
    的函数。您可以基于约定或配置(在这种情况下,您可能需要添加一个配置文件)来创建解决方案。嗯……我认为这行不通,也许我没有说清楚。top()部分中的代码将设置pyunit测试用例,中间部分将在测试用例中进行测试,而bottom()函数将完成测试用例。(如果这不是在一个班的中间,我会同意这是最好的解决方案)。@ LeIF不熟悉PyUnbe,但是你能把中间部分作为一个函数在适当的上下文中调用吗?如果它变得太复杂,最好使用
    \uuuuu import\uuuuu
    解决方案,我将每个(中间的东西)转换成一个函数(只给每个文件提供该函数),然后使用uu import\uuuu(修改的参数名称)导入文件,并调用该类中的函数。这可能涉及将所有函数放在一个文件中,或者导入每个运行时的每个模块。也许它比使用_import__()(我最终使用了它)更干净,谢谢你,下次我需要做类似的事情时,我会记住它。@Leif,正如我提到的,你的具体问题可能很简单,一个没有类、只有函数的解决方案可以很好地工作(一般来说,使用最简单且效果良好的解决方案是一个好主意;-),但我很高兴您将来喜欢模板方法的想法。