Python类的导入方法

13
我想知道是否有可能将Python类的方法保存在与类定义不同的文件中,类似于以下内容:
main_module.py:
class Instrument(Object):
    # Some import statement?
    def __init__(self):
        self.flag = True
    def direct_method(self,arg1):
        self.external_method(arg1, arg2)

to_import_from.py:

def external_method(self, arg1, arg2):
    if self.flag:
        #doing something
#...many more methods

在我的情况下,to_import_from.py是由机器生成的,包含很多方法。我不想将它们逐个复制粘贴到main_module.py中,也不想逐个导入它们,而是希望它们都被识别为Instrument类的方法,就像它们已经在那里定义一样:

>>> instr = Instrument()
>>> instr.direct_method(arg1)
>>> instr.external_method(arg1, arg2)

谢谢!


你控制 to_import_from.py 的生成,还是必须使用它“原样”? - itsadok
是的,我可以通过调整ctypeslib的代码来控制它。 - Martin Gustafsson
8个回答

19

人们似乎在过度思考。方法只是类构造函数范围内的函数值局部变量。所以以下代码可以正常工作:

class Instrument(Object):
    # load external methods
    from to_import_from import *

    def __init__(self):
        self.flag = True
    def direct_method(self,arg1):
        self.external_method(arg1, arg2)

4
语法警告:import * 只允许在模块级别导入。 - Martin Gustafsson
2
这应该是答案。 - bgusach
2
为了克服SyntaxError(Python 3.6),不要使用import *。相反,明确命名要导入的方法,例如from to_import_from import method1, method2 - afeique
当调用外部方法时,这不会将此作为第一个变量传递给类吗? - Ethan Keller

8

比你想象的更容易:

class Instrument(Object):
    def __init__(self):
        self.flag = True
    def direct_method(self,arg1):
        self.external_method(arg1, arg2)

import to_import_from

Instrument.external_method = to_import_from.external_method

完成了!

虽然让机器生成代码并从中继承一个类定义会是更整洁的解决方案。


7
我认为直接在Python中实现你想要的功能是不可能的。但你可以尝试以下两种方法之一:
1. 在生成to_import_from.py时,将非自动生成的内容添加到其中。这样,所有方法都在同一个类定义中。
2. 让to_import_from.py包含一个基类定义,Instrument类继承该基类。
换句话说,在to_import_from.py中:
class InstrumentBase(object):
    def external_method(self, arg1, arg2):
        if self.flag:
            ...

接着在 main_module.py 中:

import to_import_from

class Instrument(to_import_from.InstrumentBase):
    def __init__(self):
        ...

5
抱歉,这有点像"您不应该在墙上钉钉子"的回答,但您错过了Python类定义的重点。 您应该将包含所有方法的类放在自己的Python文件中,在main_module.py中执行。
from instrument import Instrument

如果您计划将这些方法用于多个类,应考虑创建子类。在您的情况下,机器生成的文件可以包含Instrument继承的基类。
最后,请给您的类一个良好的文档字符串,以向用户解释API,因此不需要使用“头文件”作为类的概述。

5

你可以使用__getattr__方法来完成这个操作

external.py

def external_function(arg):
    print("external", arg)

main.py:

import external

class Instrument(object):
    def __getattr__(self, name):
        if hasattr(external, name):
            return getattr(external, name)
        else:
            return Object.__getattr__(self, name)

    def direct_method(self, arg):
        print("internal", arg)


i = Instrument() 
i.direct_method("foo")
i.external_function("foo")

2
你正在使用一些“机器生成”的代码扩展一个基类。
选项1:使用机器生成的代码扩展基类。
machine_generated.py
# Start of boilerplate #
import main_module
class Instrument_Implementation( main_module.Instrument_Abstraction ):
    def direct_method(self,arg1): 
       # End of boilerplate #
       ...the real code...

您的应用程序可以使用import machine_generated,并使用machine_generated.Instrument_Implementation

选择2. 简单地使用一级函数。

machine_generated.py

def external_method(self, arg1, arg2):
    ...the real code...

main_module.py

import machine_generated

class Instrument( object ):
    def direct_method(self,arg1): 
        return machine_generator.external_method( arg1, ... )

您的应用程序可以导入main_module并使用main_module.Instrument


0

这是我的尝试。我认为可以使用元类来实现更好的方法...

to_import_from.py :

def external_method(self, arg1, arg2):
    if self.flag:
        print "flag is set"
    else :
        print "flag is not set"

instrument.py:

import imp
import os
import inspect
import new

import pdb

class MethodImporter(object) :
    def __init__(self, path_to_module) :
        self.load_module(path_to_module)

    def load_module(self, path_to_module) :
        name = os.path.basename(path_to_module)
        module_file = file(path_to_module,"r")
        self.module = imp.load_module(name, module_file , path_to_module, ('','r',imp.PY_SOURCE))
        print "Module %s imported" % self.module
        for method_name, method_object in inspect.getmembers(self.module, inspect.isfunction) :
            print "adding method %s to %s" % (method_name, self)
            setattr(self, method_name, new.instancemethod(method_object, self, self.__class__))


class Instrument(MethodImporter):
    def __init__(self):
        super(Instrument,self).__init__("./to_import_from.py")
        self.flag = True
    def direct_method(self,arg1):
        self.external_method(arg1, arg2)

当你运行这段代码时

arg1, arg2 = 1, 2
instr = Instrument()
instr.direct_method(arg1)
instr.external_method(arg1, arg2)

这是输出结果:

Module <module 'to_import_from.py' from './to_import_from.pyc'> imported
adding method external_method to <__main__.Instrument object at 0x2ddeb0>
flag is set
flag is set

0

从技术上讲,这是可能的,但用这种方式解决问题并不是真正符合Python的惯用方式,而且可能有更好的解决方案。以下是一个示例:

import to_import_from

class Instrument(object):
    locals().update(dict((k,v) for (k,v) in 
                    to_import_from.__dict__.iteritems() if callable(v)))

    def __init__(self):
        self.flag = True
    def direct_method(self,arg1):
        self.external_method(arg1, arg2)

那将把 to_import_from 中定义的所有可调用函数作为 Instrument 类的方法导入,以及添加一些其他方法。注意:如果您还想将全局变量复制为实例变量,则需要完善检查。还要注意,它会将 to_import_from 命名空间中找到的所有可调用对象添加进去,包括从其他模块导入的内容(即 from module import some_func 风格的导入)

然而,这不是一个很好的方法。更好的方法是调整代码生成来 生成 一个类,并让您的类继承它。这避免了将方法不必要地复制到 Instrument 的命名空间中,并使用普通继承。即:

class Instrument(to_import_from.BaseClass):
    # Add new methods here.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接