2

さて、これは少し複雑です。

パッケージ内にモジュールがあるとしましょう:

a_package
 |-- __init__.py
 |-- a_module.py

内部a_module.pyで私は宣言しA_Classます:

# file location: a_package/a_module.py
class A_Class():
    def say(self):
        print ("cheese")

これを行うことで、インスタンスを作成してメソッドA_Classを呼び出すことができます。say

from a_package.a_module import A_Class
my_object = A_Class()
my_object.say() # this will display 'cheese' as expected

ただし、より動的なアプローチを行いたい (パッケージとクラスをたくさん用意する予定で、コードをより簡単に記述できるようにしたい)。だから、私はと呼ばれる関数を作りますload_class

def load_class(package_name, module_name, class_name)
    result = None
    try:
        exec('from ' + package_name + '.' + module_name + ' import ' + class_name)
        exec('result = '+class_name)
    except:
        raise ImportError('Unable to load the class')
    return result

# Now, I can conveniently do this:
A_Class = load_class('a_package', 'a_module', 'A_Class')
my_object = A_Class()
my_object.say()

# or even shorter:
load_class('a_package', 'a_module', 'A_Class')().say()

プログラムは期待どおりに動作しますが、IDE (私は pydev を使用) は私のコードを理解せず、インテリセンス (コードを自動的に完成させます) を実行できません。

最初のアプローチを使用すると、インテリセンスは明らかに機能します。

from a_package.a_module import A_Class
my_object = A_Class()
my_object. # when I press ".", there will be a popup to let me choose "say" method

しかし、2 番目のアプローチを使用すると、インテリセンスは補完を行うことができません。

load_class('a_package', 'a_module', 'A_Class')(). # when I press ".", nothing happened

私は知っています、これはPythonでの動的インポートのトレードオフです。execしかし、ジェネリックIDE(Pydevなど)のインテリセンスにクラス内のメソッドを推測させることができる2番目のアプローチ(を使用していない可能性があります)を実行できる代替手段があるかどうかを知りたいですか?

編集:なぜこれを行う必要があるのですか? 私がそのようなディレクトリ構造を持っているとしましょう

fruit
 |-- strawberry.py
 |-- orange.py

chocolate
 |-- cadbury.py
 |-- kitkat.py

need_dynamic.py

need_dynamic.pyは、次のスクリプトがあります。

food_list = ['fruit', 'chocolate']
subfood_list = [['strawberry', 'orange'],['cadbury', 'kitkat']] 
# show food list and ask user to choose food
for i in xrange(len(food_list)):
    print i + " : " + food_list[i]
food_index = int(raw_input('chose one'))
# show subfood list and ask user to choose subfood 
for i in xrange(len(subfood_list[food_index])):
    print i + " : " + subfood_list[food_index][i]
subfood_index = int(raw_input('chose one'))
# init the class
my_class = load_class(food_list[food_index], subfood_list[food_index, subfood_index])
# the rest of the code

これは単純化のためです。実際には、ディレクトリを取得して自動的food_listに埋める予定です。subfood_list

データ分類フレームワークがあり、ユーザーが使用したい方法を選択できるようにしたいとします。ユーザーは、python パッケージをモジュールに追加するだけで、フレームワークを拡張することもできます。

この例が妥当であることを願っています。

受け入れられた回答をもう一度編集しても、インテリセンスの問題は解決しません。しかし、より良いコーディング方法を示しています。Pythonの問題ではなく、IDEの問題だと思います。別の質問を投稿します。

4

2 に答える 2

1

ビルトインを使用したいでしょう__import__

def load_class(package, mod_name, cls_name):
    mod = __import__('.'.join((package, mod_name)))
    return getattr(mod, cls_name)

もちろん、必要に応じてエラー処理をそこに戻すこともできますが、正直なところ、そもそもなぜこれを行う必要があるのか​​ よくわかりません。動的インポートは、ほとんどの場合、「コードの匂い」のように思えます。外出して使用を開始する前に、これが本当に必要かどうかを評価してください。

于 2014-02-01T02:59:06.843 に答える
0

ここにあなたの解決策があります:

名前を文字列として使用してモジュールをインポートする場合:

__import__(modulename, globals(), locals(), ['*'])

モジュール パスからクラスをロードします。

cls = getattr(sys.modules[modulename], classname)

ディレクトリ構造:

:/tmp/dynamic_import:~ ls
chocolate   fruit       need_dynamic.py
:/tmp/dynamic_import:~

need_dynamic.py

fruit
  |-- strawberry.py
  |-- orange.py

chocolate
 |-- cadbury.py
 |-- kitkat.py

これはフルーツ内のモジュールの1つです。モジュール名に関して、頭文字が大文字のクラス名に名前を付けます。

:/tmp/dynamic_import:~ cat orange.py
class Orange(object):
    def say(self):
        return "Say cheese from class: %s" % __name__
:/tmp/dynamic_import:~

メインスクリプトは次のとおりです。

#!/usr/bin/env python

import os
import sys
import inspect

def load_modules_from_path(path):
    """
    Import all modules from the given directory
    """
    # Check and fix the path
    if path[-1:] != '/':
        path += '/'

    # Get a list of files in the directory, if the directory exists
    if not os.path.exists(path):
         raise OSError("Directory does not exist: %s" % path)

    # Add path to the system path
    sys.path.append(path)
    # Load all the files in path
    for f in os.listdir(path):
        # Ignore anything that isn't a .py file
        if len(f) > 3 and f[-3:] == '.py':
            modname = f[:-3]
            # Import the module
            __import__(modname, globals(), locals(), ['*'])

def load_class_from_name(fqcn):

    # fqcn = fully qualified classname
    # Break apart fqcn to get module and classname

    paths = fqcn.split('.')
    modulename = '.'.join(paths[:-1])
    classname = paths[-1]

    # Import the module
    __import__(modulename, globals(), locals(), ['*'])

    # Get the class
    cls = getattr(sys.modules[modulename], classname)
    # Check cls
    if not inspect.isclass(cls):
        raise TypeError("%s is not a class" % fqcn)

    # Return class
    return cls

def main():

    food_list = ['fruit', 'chocolate']
    subfood_list = [['strawberry', 'orange'],['cadbury', 'kitkat']]

    # show food list for users to select.
    for i in xrange(len(food_list)):
        print '%d: %s' % (i, food_list[i])

    food_index = int(raw_input('Choose one: '))

    for i in xrange(len(subfood_list[food_index])):
        print '%d: %s' % (i, subfood_list[food_index][i])

    subfood_index = int(raw_input('Chose one: '))
    pkg = food_list[food_index]
    module = subfood_list[food_index][subfood_index]
    class_name = module.title()

    load_modules_from_path(pkg)
    new_class = load_class_from_name('%s.%s' % (module, class_name))

    # instantiation
    obj = new_class()
    print obj.say()

if __name__ == '__main__': main()

出力は次のとおりです。

:/tmp/dynamic_import:~ python need_dynamic.py
0: fruit
1: chocolate
Choose one: 0
0: strawberry
1: orange
Chose one: 0
Say cheese from class: strawberry
:/tmp/dynamic_import:~ python need_dynamic.py
0: fruit
1: chocolate
Choose one: 1
0: cadbury
1: kitkat
Chose one: 0
Say cheese from class: cadbury
:/tmp/dynamic_import:~

それがうまくいくかどうか教えてください。

于 2014-02-01T04:36:38.227 に答える