importing “pyc” into existing python script

2019-08-31 04:17发布

问题:

I'm trying to add a directory where I can throw some pyc files and call them from my script.

Basically it's an independent python script with a bunch of def's in it, I want the name of the pyc file to be the command executed. so far this works ok except for one thing, my GUI buttons are calling defs as they are and python doesn't recognize them since the name space is wrong.

How can I import it in from the script as if it was imported like :

from Module import as *

I'm running this code to load it in:

def importModule(ModPath):
    fullModName= ModPath.split('\\')[-1]
    commandtorun=fullModName.split('.')[0]

    mod_name,file_ext = os.path.splitext(os.path.split(ModPath)[-1])
    if file_ext.lower() == '.py':
        py_mod = imp.load_source(mod_name, ModPath)
    elif file_ext.lower() == '.pyc':
        py_mod = imp.load_compiled(mod_name, ModPath)

    exec "from %s import *"%(commandtorun)
    exec "%s()" % (commandtorun)

i know i can write : module.somefunction() , but that's not what i need... i need the "module" to work independently..

Please help, this is driving me crazy, python is such an awesome language I cant believe you can't source a script from within a script and have it work. please advise.

回答1:

  • You could use a (rare-in-Python) semicolon in your exec call to make one exec out of the two you have
  • You might want to take a look at import_module(name, package) in importlib


回答2:

AFAIK exec has it's own variable scope, thereby importing your script into a scope that is immediately destroyed. You could either pass in dicts for the globals and locals or avoid exec completely, which would be my suggestion. Why don't you just use from ... import * directly?

If you place an __init__.py file in the directory, you can use from folder.script import * and if the folder is not in your current working directory or your path, you

  1. are probably doing something wrong, you should have one project directory (that can of course have subdirectories) where your code is; or create multiple modules that you install seperately and make sure the ones you need are in your path
  2. can add any directory to your path, so you can import modules directly from it with

    import sys
    sys.path.append("/path/to/directory")
    


回答3:

If you want to auto-exec a def inside an already imported module, you can do it more simply with the inspect module:

import inspect
def auto_exec (module):
    for fn_name, fn in inspect.getmembers(module, isfunction):
        if fn_name == module.__name__:  fn()

However that's a bandaid on the real problem.

Popping things into the global namespace is an invitation to problems because there is now way to be sure that the function named X you're calling from the global space is the X you want - lots of different modules use similar function names and without namespaces you can't be sure you're getting what you want. It's like voluntarily embracing Javascript style global bugs.

It sounds like you're calling the scripts mel-style with strings? Don't. In python you want to use 'import' to initialize modules and make them available, and it's fine to import the same module multiple times in multiple places. You want to do it without the * so you keep things in predictable order: This is fine python but would be impossible with * imports:

 import legs
 import arms

 legs.rig(cmds.ls("*leg"))
 arms.rig(cmds.ls("*arm"))

As for calling the functions in GUI, the global namespace only matters if you're trying to call the function with strings. Pass in the python functions directly as functions:

 def example(*args):
     print args

 b = cmds.button("test", c= example) # this could also be "somemodule.example" if you import somemodule

As long as example is defined at the time you create the button, this works without any global namespace monkeying. It's very common to manage this using a class so that the callback goes to a class function: that makes it really easy to manage what's going on.

BTW, If you need to pass arguments to the gui calls, the cheap way to do this is to use functools.partial which is designed for bundling args and callables just this way:

from functools import partial
import my_module 

# somewhere in gui

my_button = cmds.button ("test", c = partial(my_module.my_function, "this works"))

There's a bunch of discussion on this problem over at Tech-Artists.org