- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
主要目标:在运行时使用该字符串动态创建的工厂中自动注册类(通过字符串),类可以在它们自己的文件中,而不是分组在一个文件中。
我有几个类都继承自同一个基类,它们定义了一个字符串作为它们的类型。
用户想要获取其中一个类的实例,但只知道运行时的类型。
因此我有一个工厂来创建给定类型的实例。我不想硬编码“if then 语句”,所以我有一个元类来注册基类的所有子类:
class MetaRegister(type):
# we use __init__ rather than __new__ here because we want
# to modify attributes of the class *after* they have been
# created
def __init__(cls, name, bases, dct):
if not hasattr(cls, 'registry'):
# this is the base class. Create an empty registry
cls.registry = {}
else:
# this is a derived class. Add cls to the registry
interface_id = cls().get_model_type()
cls.registry[interface_id] = cls
super(MetaRegister, cls).__init__(name, bases, dct)
问题是要使它工作,工厂必须导入所有子类(因此元类运行)。要解决此问题,您可以使用 from X import *
但要使其正常工作,您需要在包的 __init__.py
文件中定义一个 __all__
var 以包含所有子类。
我不想对子类进行硬编码,因为它违背了使用元类的目的。
我可以使用以下方法查看包中的文件:
import glob
from os.path import dirname, basename, isfile
modules = glob.glob(dirname(__file__) + "/*.py")
__all__ = [basename(f)[:-3] for f in modules if isfile(f)]
效果很好,但是项目需要编译成单个 .so 文件,这会导致文件系统的使用无效。
那么我怎样才能在不硬编码类型的情况下实现在运行时创建实例的主要目标呢?
有没有办法在运行时填充 __all__
var 而无需触及文件系统?
在 Java 中,我可能会用注解装饰类,然后在运行时获取所有带有该注解的类,在 python 上有类似的东西吗?
我知道 python 中有装饰器,但我不确定我能否以这种方式使用它们。
编辑 1:每个子类都必须在一个文件中:
- Models
-- __init__.py
-- ModelFactory.py
-- Regression
--- __init__.py
--- Base.py
--- Subclass1.py
--- Subclass2ExtendsSubclass1.py
编辑 2:说明问题的一些代码:
+ main.py
|__ Models
|__ __init__.py
|__ ModelFactory.py
|__ Regression
|__ init__.py
|__ Base.py
|__ SubClass.py
|__ ModelRegister.py
main.py
from models.ModelFactory import ModelFactory
if __name__ == '__main__':
ModelFactory()
ModelFactory.py
from models.regression.Base import registry
import models.regression
class ModelFactory(object):
def get(self, some_type):
return registry[some_type]
ModelRegister.py
class ModelRegister(type):
# we use __init__ rather than __new__ here because we want
# to modify attributes of the class *after* they have been
# created
def __init__(cls, name, bases, dct):
print cls.__name__
if not hasattr(cls, 'registry'):
# this is the base class. Create an empty registry
cls.registry = {}
else:
# this is a derived class. Add cls to the registry
interface_id = cls().get_model_type()
cls.registry[interface_id] = cls
super(ModelRegister, cls).__init__(name, bases, dct)
Base.py
from models.regression.ModelRegister import ModelRegister
class Base(object):
__metaclass__ = ModelRegister
def get_type(self):
return "BASE"
SubClass.py
from models.regression.Base import Base
class SubClass(Base):
def get_type(self):
return "SUB_CLASS"
运行它你只能看到它打印的“Base”。使用装饰器会得到相同的结果。
最佳答案
将类注册为运行时的一种简单方法是使用装饰器:
registry = {}
def register(cls):
registry[cls.__name__] = cls
return cls
@register
class Foo(object):
pass
@register
class Bar(object):
pass
如果您的所有类都定义在同一个模块中,并且该模块是在运行时导入的,那么这将起作用。但是,您的情况使事情复杂化。首先,您想在不同的模块中定义您的类。这意味着我们必须能够在运行时动态确定包中存在哪些模块。使用 Python 的 pkgutil
模块会很简单,但是,您还声明您正在使用 Nuitka 将包编译成扩展模块。 pkgutil
不适用于此类扩展模块。
我找不到任何记录在案的方法来从 Python 中确定 Nuitka 扩展模块中包含的模块。如果确实存在,上面的装饰器方法将在动态导入每个子模块后起作用。
事实上,我认为最直接的解决方案是在编译之前编写一个脚本来生成一个__init__.py
。假设我们有如下包结构:
.
├── __init__.py
├── plugins
│ ├── alpha.py
│ └── beta.py
└── register.py
“插件”包含在 plugins
目录中。文件内容为:
# register.py
# -----------
registry = {}
def register(cls):
registry[cls.__name__] = cls
return cls
# __init__.py
# -----------
from . import plugins
from . import register
# ./plugins/alpha.py
# ------------------
from ..register import register
@register
class Alpha(object):
pass
# ./plugins/beta.py
# ------------------
from ..register import register
@register
class Beta(object):
pass
就目前而言,导入上面的包不会导致任何类被注册。这是因为类定义永远不会运行,因为包含它们的模块永远不会被导入。解决办法是为plugins
文件夹自动生成一个__init__.py
。下面是一个执行此操作的脚本 -- 这个脚本可以成为您编译过程的一部分。
import pathlib
root = pathlib.Path('./mypkg/plugins')
exclude = {'__init__.py'}
def gen_modules(root):
for entry in root.iterdir():
if entry.suffix == '.py' and entry.name not in exclude:
yield entry.stem
with (root / '__init__.py').open('w') as fh:
for module in gen_modules(root):
fh.write('from . import %s\n' % module)
将此脚本放在包根目录之上的一个目录(假设您的包名为 mypkg
)并运行它会产生:
from . import alpha
from . import beta
现在进行测试:我们编译包:
nuitka --module mypkg --recurse-to=mypkg
并尝试导入它,检查是否所有类都已正确注册:
>>> import mypkg
>>> mypkg.register.registry
{'Beta': <class 'mypkg.plugins.beta.Beta'>,
'Alpha': <class 'mypkg.plugins.alpha.Alpha'>}
请注意,同样的方法也适用于使用元类来注册插件类,我只是更喜欢在这里使用装饰器。
关于Python 元类和导入 *,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34160529/
当我这样做时... import numpy as np ...我可以使用它但是... import pprint as pp ...不能,因为我需要这样做... from pprint import
我第一次尝试将 OpenCV 用于 Python 3。要安装,我只需在终端中输入“pip3 install opencv-python”。当我这样做时,我在 Finder(我在 Mac 上)中看到,在
如果有一个库我将使用至少两种方法,那么以下之间在性能或内存使用方面是否有任何差异? from X import method1, method2 和 import X 最佳答案 有区别,因为在 imp
我正在从 lodash 导入一些函数,我的同事告诉我,单独导入每个函数比将它们作为一个组导入更好。 当前方法: import {fn1, fn2, fn3} from 'lodash'; 首选方法:
之间有什么关系: import WSDL 中的元素 -和- import元素和在 XML Schema ...尤其是 location 之间的关系前者和 schemaLocation 的属性后者的属性
我在从 'theano.configdefaults' 导入 'local_bitwidth' 时遇到问题。并显示以下消息: ImportError
我注意到 React 可以这样导入: import * as React from 'react'; ...或者像这样: import React from 'react'; 第一个导入 react
对于当前的项目,我必须使用矩阵中提供的信息并对其进行数学计算,以及使用 ITK/VTK 函数来显示医疗信息/渲染。基本上我必须以(我猜)50/50 的方式同时使用 matlab 例程和 VTK/ITK
当我看到 pysqlite 的示例时,SQLite 库有两个用例。 from sqlite3 import dbapi2 as sqlite3 和 import sqlite3 为什么有两种方式支持s
我使用 Anaconda Python 发行版:Python 2.7 x64 和 Windows 7 SP1 x64 Ultimate。 当我import matplotlib.pyplot时,我得到
目录 【容器】镜像导出/导入 导出 导入 带标签 不带标签,后期修改 【仓库】镜像导出/导入
我正在寻找一种导入模块的方法,以便我可以从子文件夹 project/v0 和根文件夹 project 运行脚本。/p> 我在 python 3.6 中的文件结构(这就是没有初始化文件的原因) proj
我通常被告知以下是不好的做法。 from module import * 主要原因(或者有人告诉我)是,您可能会导入一些您不想要的东西,并且它可能会隐藏另一个模块中具有类似名称的函数或类。 但是,Py
我为 urllib (python3) 编写了一个小包装器。在if中导入模块是否正确且安全? if self.response_encoding == 'gzip': import gzip
我正在 pimcore 中创建一个新站点。有没有办法导出/导入 pimcore 站点的完整数据,以便我可以导出 xml/csv 格式的 pimcore 数据进行必要的更改,然后将其导入回来? 最佳答案
在 Node JS 中测试以下模块布局,看起来本地导出的定义总是在名称冲突的情况下替换外部导出的定义(参见 B.js 中的 f1)。 A.js export const f1 = 'A' B.js e
我在使用 VBA 代码时遇到了一些问题,该代码应该将 excel 数据导入我的 Access 数据库。当我运行代码时,我收到一个运行时错误“运行时错误 438 对象不支持此属性或方法”。来自我在其他论
我有一个名为 elements 的包,其中包含按钮、trifader、海报等内容。在 Button 类中,我正在执行 from elements import * 这执行正常,当我尝试 print(p
在我长期使用 python 的经验中,我遇到了一个非常奇怪的问题。 提前我想说我想知道为什么会发生这种情况 ,而不是如何更改我的代码或如何修复它,因为我也可以做到。 我正在使用 python2.7.3
我正在更新我的包。但是,我正在为依赖项/导入而苦苦挣扎。我使用了两个冲突的包 - ggplot2和 psych及其功能 alpha当然还有 alpha ggplot2 的对象不同于 alpha psy
我是一名优秀的程序员,十分优秀!