Импорт методов для класса Python

интересно, можно ли сохранить методы для класса 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)

спасибо!

8 ответов


Я не думаю, что то, что вы хотите, непосредственно возможно в Python.

вы можете, однако, попробовать одно из следующих действий.

  1. при создании to_import_from.py, добавьте туда и не сгенерированный материал. Именно такой образ, все методы находятся в одном определении класса.
  2. есть to_import_from.py содержит определение базового класса, которое класс инструмента наследует.

другими словами, в 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):
        ...

люди, кажется, слишком много об этом думаешь. Методы - это просто локальные переменные с функциональным значением в области построения класса. Таким образом, следующее отлично работает:

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)

Это проще, чем вы думаете:

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

готово!

хотя наличие машинного кода генерирует определение класса и подклассы из него было бы более аккуратным решением.


вы можете сделать это с помощью __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")

мне жаль, что это своего рода " вы не должны класть гвозди в стену " ответ, но вы упускаете суть определений классов python. Вы должны скорее поместить класс со всеми его методами в свой собственный файл python и в свой main_module.py do

from instrument import Instrument

Если вы планируете использовать методы для нескольких классов, вы должны рассмотреть подклассы. В вашем случае созданный машиной файл может содержать базовый класс, который Instrument наследует от.

наконец, дайте вашему классу хорошую docstring, которая объясняет API своему пользователю, поэтому нет необходимости в "файле заголовка", используемом в качестве обзора вашего класса.


то, что вы делаете, это расширение базового класса с помощью некоторого "машинного" кода.

выбор 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, ... )

приложение import main_module и использовать main_module.Instrument.


вот моя попытка. Думаю, с метаклассами можно было бы подойти лучше...

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

технически, да, это возможно, но решение его таким образом на самом деле не является идиоматическим 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 class, а также добавление еще нескольких методов. Примечание. Если вы также хотите скопировать глобальные переменные в качестве переменных экземпляра, вам необходимо уточнить проверку. Также обратите внимание, что он добавляет все отзывной объекты, которые он находит в пространстве имен to_import_from, включая импорт из других модулей (т. е. from module import some_func стиль импорт)

однако, это не очень хороший способ сделать это. Лучше было бы вместо этого настроить генерацию кода на производства класс, и ваш класс наследуется от него. Это позволяет избежать ненужных копирование методов в пространстве имен прибора, и вместо этого использует обычный inheritcance. т. е.:

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