【问题标题】:Adding functions from other files to a Python class将其他文件中的函数添加到 Python 类
【发布时间】:2012-03-26 01:04:32
【问题描述】:

我在这个设置上遇到了问题,主要是因为我不确定我真正想要什么来解决这个问题。

这是设置

- main.py
- lib
  - __init__.py 
  - index.py
  - test.py

__init__.py有这个代码

import os
for module in os.listdir(os.path.dirname(__file__)+"/."):
    if module == '__init__.py' or module[-3:] != '.py':
        continue
    __import__(module[:-3], locals(), globals())
del module

ma​​in.py 目前有此代码

from lib.index import *
print User.__dict__

index.py有这个代码

class User(object):
    def test(self):
        return "hi"
    pass

test.py有这个代码

class User(object):
    def tes2(self):
        return "hello"

当我执行main.py 时,它会成功打印来自index.py 的方法test,但我想做的是找出一种方法,我可以在lib 文件夹中创建一个文件,而该文件只有一个函数格式

class User(object):
    def newFunction(self):
       return abc

这个函数应该在 main.py 中自动可供我使用

我确信这不是一件难事,但老实说,我不知道我想要什么(搜索什么来解决这个问题),这阻碍了我研究解决方案。

【问题讨论】:

  • 实现 Ruby 的开放类理念导致代码难以调试。

标签: python class inheritance


【解决方案1】:

您可以使用元类来自定义类的创建并添加在别处定义的函数:

import types
import os
import os.path
import imp

class PluginMeta(type):
    def __new__(cls, name, bases, dct):
        modules = [imp.load_source(filename, os.path.join(dct['plugindir'], filename))
                    for filename in os.listdir(dct['plugindir']) if filename.endswith('.py')]
        for module in modules:
            for name in dir(module):
                function = getattr(module, name)
                if isinstance(function, types.FunctionType):
                    dct[function.__name__] = function
        return type.__new__(cls, name, bases, dct)


class User(object):
    __metaclass__ = PluginMeta
    plugindir = "path/to/the/plugindir"

    def foo(self):
        print "foo"

user = User()
print dir(user)

然后在插件文件中,只创建函数而不是类:

def newFunction(self, abc):
    self.abc = abc
    return self.abc

元类会找到它们,将它们转化为方法,并将它们附加到您的类中。

【讨论】:

  • 你好,它给了我这个错误dct[function.__name__] = types.MethodType(function) TypeError: Error when calling the metaclass bases instancemethod expected at least 2 arguments, got 1
  • @Kartik 已修复。正确的版本是types.MethodType(function, cls)
  • 非常感谢。该代码在为目录添加功能方面起作用,但我在访问它们时遇到了麻烦。在这种情况下,我似乎无法在任何文件中调用self.foo()。我得到的错误是PluginMeta has no attribute foo。不应该是User() 吗?
  • @Kartik 我不确定你的意思。唯一可以使用self 的地方是在实例方法内——要么在User 上的实例方法内,要么在PluginMeta 为你变成实例方法的插件函数内。您不能在其他任何地方调用self.foo(),并且插件文件中不应有任何其他代码。
  • @Kartik 好的,明白了。我的错。又是同一行——对MethodType 的调用是完全错误的;我很困惑。我修复了上面的代码,但只需将该行更改为 dct[function.__name__] = function 就可以了。
【解决方案2】:

类是对象,方法只不过是类对象的属性。

因此,如果您想在原始类块之外向现有类添加方法,那就是向对象添加属性的问题,我希望您知道该怎么做:

class User(object):
    pass

def newFunction(self):
    return 'foo'

User.newFunction = newFunction

agf 的元类答案基本上是一种很好的自动方法,尽管它通过在创建类之前向类块添加额外定义来工作,而不是在创建类之后向类对象添加额外属性.

这基本上应该是您开发一个框架所需的全部内容,在该框架中,一个模块中定义的内容会自动添加到其他地方定义的类中。但是您仍然需要做出一些设计决策,例如:

  1. 如果您的外部定义函数需要辅助定义,您如何确定应该添加到类中的内容以及哪些只是依赖项?
  2. 如果您有多个类以这种方式扩展,您如何确定哪些内容属于哪个类?
  3. 在您的程序中的什么时候发生自动扩展?
  4. 您想在您的课程中说“这个类在其他地方定义了扩展”,还是在您的扩展中说“这是对其他地方定义的类的扩展”,或者两者都没有,并且在某个地方从两者外部绑定到类的扩展?
  5. 您是否需要能够同时激活具有不同扩展的“相同”类的多个版本?

由 agf 提出的元类可能是实现此类框架的一种非常好的方法,因为它可以让您将所有复杂的代码放在一个地方,同时仍然“标记”每个不能像类那样工作的类正常工作。不过,它确实解决了我上面提出的一些问题的答案。

【讨论】:

    【解决方案3】:

    这是我们在项目中使用的工作代码,我不确定这是不是最好的方法,但它确实有效,几乎没有额外的代码可以添加到其他文件中

    cpu.py:

    from cpu_base import CPU, CPUBase
    
    import cpu_common
    import cpu_ext
    

    cpu_base.py:

    def getClass():
        return __cpu__
    
    def setClass(CPUClass):
        global __cpu__
        __cpu__ = CPUClass
        __classes__.append(CPUClass)
    
    def CPU(*kw):
        return __cpu__(*kw)
    
    class CPUBase:
        def __init__(self):
            your_init_Stuff
            # optionally a method classname_constructor to mimic __init__ for each one
            for c in __classes__:
                constructor = getattr(c, c.__name__ + '_constructor', None)
                if constructor is not None:
                    constructor(self)
    setClass(CPUBase)
    

    cpu_common.py:

    from cpu_base import getClass, setClass
    class CPUCommon(getClass()):
        def CPUCommon_constructor(self):
            pass
    setClass(CPUCommon)
    

    cpu_ext.py:

    from cpu_base import getClass, setClass
    
    class CPUExt(getClass()):
        pass
    setClass(CPUExt)
    

    使用类从 cpu.py 导入 CPU

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-02
      • 1970-01-01
      • 1970-01-01
      • 2012-03-08
      • 2023-03-29
      • 2021-10-06
      • 2014-08-12
      相关资源
      最近更新 更多